Pular para o conteúdo principal

Automatize a escrituração com bea

Automatize seus livros Beancount com bea: resolva o ledger explicitamente, analise o envelope JSON com jq, ramifique nos códigos de saída, execute sem supervisão e agende uma verificação noturna.

Um script dirige o bea com quatro decisões: qual ledger ele lê, --json para saída legível por máquina, jq para o valor que precisa e o código de saída no qual ele ramifica. Este guia percorre essas quatro decisões de ponta a ponta e depois as agenda.

Você precisa do bea na máquina que executa o trabalho e de um ledger que ele possa acessar. Se você está iniciando novos livros, siga o guia de início rápido da CLI primeiro. Cada fato sobre flags, chaves de envelope e códigos de saída é consultado na referência da CLI Beancount, e não repetido aqui.

Escolha o ledger explicitamente

Nomeie o arquivo. Um comando local resolve seu alvo a partir de --file, depois $BEA_FILE, depois ./main.bean no diretório de trabalho, e um trabalho agendado raramente executa onde você pensa que executa.

bea --file ~/books/main.bean check
BEA_FILE=~/books/main.bean bea check
cd ~/books && bea check

Opções globais vão antes do comando, como em bea --file main.bean check. Se o arquivo resolvido não existir, o comando sai com 2 e nomeia todas as três fontes, então um erro de digitação em uma entrada cron falha ruidosamente em vez de validar os livros errados. Endereçamento hospedado através de uma 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 para comparar sem que um float entre no pipeline. As chaves do envelope são tabuladas na referência de JSON e códigos 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 suas formas 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 se basear no status de saída de check em vez de sua forma de saída. Escolha sua política de duplicados deliberadamente: --duplicates ainda é necessário quando uma importação precisa de uma decisão, como o passo a passo de importação explica.

Pare no código de saída correto

Ramifique no status e leia o objeto de erro antes de tentar novamente qualquer coisa que escreve. 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" ;;
esac

Saída 4 é aquela que um script nunca deve tentar novamente às cegas: significa que o resultado é um conflito ou é desconhecido, como uma edição externa chegando no meio da escrita ou um alvo init que já existe. Inspecione o ledger e depois tente novamente a partir de uma leitura nova. Saída 1 cobre falhas de validação e qualquer outro erro de runtime; error.details carrega os erros individuais do ledger, e error.result carrega o que uma escrita parcial realmente fez. Uma saída diferente de zero nunca garante que nada mudou.

Execute sem um terminal

bea para de pedir confirmação por conta própria. --no-input é implícito sempre que stdin não é um terminal, sempre que --json está definido e sempre que CI é truthy — 1, true, yes ou on. Nesse modo, uma confirmação ausente falha com saída 2 em vez 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 oldest

Leituras são tolerantes em um terminal e estritas em qualquer outro lugar. Quando o ledger tem erros de carregador, query, list e report saem com 1 sob --json, sob stdout canalizado, sob CI truthy, ou com --strict; passe o próprio --allow-errors do comando para aceitar a resposta parcial em vez disso, o que também define ledger_valid: false e preenche ledger_errors no JSON. --strict é a imagem espelhada: recusa respostas parciais mesmo em um terminal, o que é o que você quer quando um humano executa o mesmo script manualmente. bea check não tem --allow-errors — relatar erros é seu trabalho inteiro — e sempre sai com 1 quando encontra qualquer um. Defina BEA_NO_UPDATE_NOTIFIER=1 para silenciar o aviso de atualização passivo; um CI truthy já faz isso.

Agende 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, o cronograma 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 check
name: 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.1.0
      - run: bea --file main.bean check
      - run: bea --json --file main.bean report balance-sheet > balance-sheet.json

Fixe a versão quando o trabalho deve ser reproduzível e remova a fixação quando você preferir rastrear lançamentos. CI já é truthy no GitHub Actions, então prompts estão desligados e o aviso de atualização é silencioso antes de você definir qualquer coisa. Não há etapa de formatação aqui de propósito. Um trabalho agendado não deve reescrever arquivos que não precisava, então use bea format --check em um hook de pre-commit, que não toca em nada e sai com 1 quando um arquivo precisa de formatação.

Use uma credencial hospedada em um trabalho

Defina BEA_TOKEN, do armazenamento secreto do seu provedor de CI, e pule o login no navegador inteiramente. O token é lido do ambiente e nunca escrito em disco, então nada cai no diretório inicial do executor para o próximo trabalho encontrar.

export BEA_TOKEN="$YOUR_CI_SECRET"
bea --json cloud status

Saída 0 significa que a credencial foi resolvida e o envelope nomeia a conta à qual pertence; saída 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 a um token fornecido dessa forma — ele não o revoga nem o desdefine, já que outro trabalho pode compartilhá-lo — então revogue um token vazado do painel em vez disso. Comandos locais não precisam de credencial alguma; apenas bea cloud e bea ask alcançam 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 precisa de um humano, e um bea cloud logout ou bea cloud ledger clone bem-sucedido não retorna objeto de sucesso JSON — leia o status de saída deles em vez disso. Saída de ajuda, versão e conclusão de shell permanece textual.

Próximos passos

Fonte: https://beancount.io/pt/docs/Solutions/automate-bookkeeping-with-bea