Um script conduz o bea com quatro decisões: qual razão ele lê, --json para saída legível por máquina, jq para o valor que ele precisa e o código de saída em que ele ramifica. Este guia percorre essas quatro decisões de ponta a ponta, depois as agenda.
Você precisa do bea na máquina que executa o trabalho e de um razão que ela possa acessar. Se estiver começando livros novos, siga o tutorial rápido da CLI primeiro. Todo fato sobre flags, chaves de envelope e códigos de saída é consultado no referência da CLI Beancount, não repetido aqui.
Escolha o razão explicitamente
Nomeie o arquivo. Um comando local resolve seu destino a partir do --file, depois do $BEA_FILE, depois do ./main.bean no diretório de trabalho, e um trabalho agendado raramente roda onde você pensa que roda.
bea --file ~/books/main.bean check
BEA_FILE=~/books/main.bean bea check
cd ~/books && bea checkOpções globais vão antes do comando, como no bea --file main.bean check. Se o arquivo resolvido não existir, o comando sai com 2 e nomeia as três fontes, então um erro de digitação numa entrada do cron falha estrondosamente ao invés de validar livros errados. O direcionamento hospedado via flag --ledger ainda não existe; bea nunca envia um arquivo local implicitamente.
Leia o envelope JSON
Adicione o --json global e todo comando suportado responde com o mesmo envelope: bea, target, data, truncated e limit em listas limitadas. Valores são strings decimais e datas são ISO YYYY-MM-DD, então um valor é seguro de comparar sem que um float entre na linha de análise. As chaves do envelope são tabeladas na referência JSON e código de saída.
bea --json --file main.bean report income-statement | jq .data.net_profit
bea --json --file main.bean list transaction --limit 2 | jq '.data[0].postings[0].units'
bea --json --file main.bean import statement.csv --csv date=Date,amount=Amount,payee=Payee \
--account Assets:Checking --apply --duplicates skip | jq '.data | {written, ready, duplicates}'Esses três comandos — report, list e import — mantêm seus formatos de resultado, então um caminho jq escrito contra eles permanece válido. bea --json check e bea --json query também emitem o envelope hoje, mas são os comandos entregues aos executáveis nativos do Beancount, então um script deve basear-se no status de saída do check em vez da forma da saída. Escolha sua política de duplicação deliberadamente: --duplicates ainda é necessário quando uma importação exige decisão, como o tutorial de importação explica.
Pare no código de saída correto
Ramine sobre o status, e leia o objeto de erro antes de tentar novamente qualquer operação que escreva. No modo --json, uma falha não escreve nada no stdout e exatamente um objeto no stderr, cujo error.category nomeia a classe: validation (1), usage (2), auth (3), conflict (4).
#!/usr/bin/env bash
set -euo pipefail
out=$(mktemp)
err=$(mktemp)
status=0
bea --json --file main.bean report income-statement >"$out" 2>"$err" || status=$?
case "$status" in
0) jq -r '.data.net_profit | to_entries[] | "net profit: \(.value) \(.key)"' "$out" ;;
4) echo "conflict — inspect the ledger before retrying" >&2
jq -r '.error.message' "$err" >&2
exit 4 ;;
*) jq -r '.error | "\(.category) (exit \(.exit_code)): \(.message)"' "$err" >&2
exit "$status" ;;
esacExit 4 é aquele que um script nunca deve tentar novamente cegamente: significa que o resultado é um conflito ou é desconhecido, como uma edição externa chegando no meio da gravação ou um alvo init que já existe. Inspecione o razão, então tente novamente a partir de uma leitura nova. Exit 1 cobre falhas de validação e qualquer outro erro em tempo de execução; error.details transporta os erros individuais do razão, e error.result transporta o que uma gravação parcial realmente fez. Um exit diferente de zero nunca garante que nada mudou.
Executar sem um terminal
bea cessa o prompt por conta própria. --no-input é implícito sempre que stdin não é um terminal, sempre que --json está definido, e sempre que CI é verdadeiro — 1, true, yes ou on. Nesse modo, uma confirmação ausente falha com exit 2 ao invés de esperar para sempre.
CI=true BEA_NO_UPDATE_NOTIFIER=1 bea --json --file main.bean report balance-sheet
bea --json --file main.bean list transaction --limit 100 --sort oldestLeituras são permissivas em um terminal e rígidas em qualquer outro lugar. Quando o razão tem erros de carregador, query, list e report saem com exit 1 sob --json, sob um stdout canalizado, sob um CI verdadeiro, ou com --strict; passe o próprio --allow-errors do comando para aceitar a resposta parcial em vez disso, que também define ledger_valid: false e preenche ledger_errors no JSON. --strict é o inverso: recusa respostas parciais mesmo em um terminal, que é o que você quer quando um humano executa o mesmo script manualmente. bea check não tem --allow-errors — reportar erros é sua tarefa inteira — e sempre sai com exit 1 quando encontra algum. Defina BEA_NO_UPDATE_NOTIFIER=1 para silenciar o aviso de atualização passiva; um CI verdadeiro já o faz.
Agendar uma verificação
Execute uma validação toda noite e deixe o código de saída ser o alerta. Ambos os blocos abaixo são modelos — os caminhos, a agenda e o executor são seus.
# crontab -e — 07:15 daily; cron mails you only when bea exits nonzero
15 7 * * * BEA_NO_UPDATE_NOTIFIER=1 /opt/homebrew/bin/bea --file /home/alice/books/main.bean checkname: ledger
on:
schedule:
- cron: "15 7 * * *"
push:
jobs:
check:
runs-on: ubuntu-latest
env:
BEA_NO_UPDATE_NOTIFIER: "1"
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- run: uv tool install beancount-io==0.2.0
- run: bea --file main.bean check
- run: bea --json --file main.bean report balance-sheet > balance-sheet.jsonO primeiro comando com motor próprio baixa o motor gerenciado, então o executor precisa de acesso à rede. Para reutilizá-lo entre trabalhos, armazene em cache ~/.local/share/bea/engine com uma chave contendo o sistema operacional do executor, arquitetura, versão do Python e versão fixada do bea. Fixe a versão quando o trabalho deve ser reproduzível, e remova a fixação quando preferir acompanhar lançamentos. CI já é verdadeiro no GitHub Actions, então prompts estão desligados e o aviso de atualização está silencioso antes de você configurar qualquer coisa. Não há etapa de formatação aqui de propósito. Um trabalho agendado não deve reescrever arquivos que não teve que alterar, então opte por bea format main.bean --check em um gancho pre-commit, que não toca nada e sai com exit 1 quando um arquivo precisa de formatação.
Use uma credencial hospedada em um trabalho
Defina BEA_TOKEN, a partir do cofre secreto do seu provedor de CI, e pule completamente o login no navegador. O token é lido do ambiente e nunca é gravado no disco, portanto, nada é armazenado no diretório home do runner para o próximo trabalho encontrar.
export BEA_TOKEN="$YOUR_CI_SECRET"
bea --json cloud statusExit 0 significa que a credencial foi resolvida e o envelope nomeia a conta a que pertence; sair com 3 com error.category de auth significa que não foi, e a mensagem distingue uma credencial não definida de uma rejeitada. bea cloud logout não faz nada com um token fornecido desta maneira — ele não o revoga nem o anula, pois outro trabalho pode compartilhá-lo — portanto, revogue um token vazado a partir do painel. Comandos locais não precisam de credencial alguma; somente bea cloud e bea ask acessam o serviço hospedado. A lista completa de variáveis está na referência de configurações.
Nem tudo responde em JSON. bea ask rejeita o modo JSON imediatamente, bea cloud login necessita de intervenção humana, e um bea cloud logout ou bea cloud ledger clone bem-sucedido não retorna objeto JSON de sucesso — leia o status de saída deles em vez disso. Saída de ajuda, versão e conclusão de shell permanecem textuais.
Próximos passos
- Automatize arquivos bancários com o tutorial de importação CLI.
- Consulte qualquer flag, chave de envelope ou código de saída na referência CLI do Beancount.
- Use Python apenas quando o CLI não for suficiente: veja workflows scriptáveis.