Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/publicar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,8 @@ jobs:
docker logs fumaca-worker 2>&1 | grep -c '"backend":"cgo"' | grep -q '^5$' \
|| { echo "::error::a lib nativa não carregou nos 5 serviços"; docker logs fumaca-worker; exit 1; }
echo "fumaça OK"

# A fumaça prova que a lib carrega, não qual lib é. A v1.3.0 subiu e passou
# na fumaça com a lib de NFS-e sem os patches que o próprio release trazia.
- name: as libs da imagem são as do release
run: scripts/conferir-libs-imagem.sh "${IMAGEM}:${GITHUB_SHA}" acbr-libs
27 changes: 27 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# O publicar.yml empurra :<sha> antes de conferir, e daqui só se sabe que
# a imagem existe, não que aquele workflow passou. Conferir de novo aqui é
# o que impede promover uma imagem com lib diferente da do release.
- name: baixa as libs do release
run: make acbr-libs-baixar

- name: as libs da imagem são as do release
run: scripts/conferir-libs-imagem.sh "${IMAGEM}:${GITHUB_SHA}" acbr-libs

- name: promove :<sha> → :vX.Y.Z (+ :stable)
run: |
set -eu
Expand Down Expand Up @@ -68,6 +77,18 @@ jobs:
docker buildx imagetools inspect "${IMAGEM}:${VERSAO}"
echo "tags publicadas: $(echo "$TAGS" | tr ' ' '\n' | grep -v '^-t$' | tr '\n' ' ')"

# Conferir, não presumir: cada tag nova tem de estar no digest de origem.
digest() { docker buildx imagetools inspect "$1" --format '{{json .Manifest}}' | jq -r .digest; }
DIGEST="$(digest "$ORIGEM")"
for t in $(echo "$TAGS" | tr ' ' '\n' | grep -v '^-t$'); do
d="$(digest "$t")"
if [ "$d" != "$DIGEST" ]; then
echo "::error::$t está em $d, a origem $ORIGEM está em $DIGEST"
exit 1
fi
done
echo "DIGEST=$DIGEST" >> "$GITHUB_ENV"

# Promover a imagem sem criar a página deixa a aba de Releases atrás das
# tags, e quem abre o repositório vê como "Latest" o que estiver lá, que
# já foi um pacote de binários de biblioteca.
Expand Down Expand Up @@ -109,13 +130,19 @@ jobs:
echo "anexos da ACBrLib conferidos em $LIBS_REL"
fi

NFSE_SHA="$(awk '$2 ~ /libacbrnfse64\.so$/ {print $1}' acbr-libs/SHA256SUMS)"

{
printf 'Imagem: `%s:%s`' "$IMAGEM" "$VERSAO"
case "$VERSAO" in
*-*) printf ' (pré-release: não move `:stable` nem a tag maior)\n' ;;
*) printf ', que é o mesmo digest de `:%s` e `:stable`.\n' "${VERSAO%%.*}" ;;
esac
printf '\n'
# O digest não muda quando alguém move uma tag. É ele que se fixa no
# deploy: image: ...@sha256:...
printf 'Digest: `%s@%s`\n\n' "$IMAGEM" "$DIGEST"
printf 'Lib de NFS-e na imagem: `libacbrnfse64.so` sha256 `%s`, conferida contra o `SHA256SUMS` do release das libs.\n\n' "$NFSE_SHA"
if [ -n "$LIBS_REL" ]; then
if [ "$LIBS_REL" = "$VERSAO" ]; then
printf 'Binários da ACBrLib (revisão r%s): anexos deste release.\n\n' "$ACBR_R"
Expand Down
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@ acbr-libs-conferir:
if [ "$$falta" = 1 ]; then echo; echo "Rode 'make acbr-libs-baixar'."; exit 1; fi
@echo "acbr-libs OK"

## imagem-libs-conferir: confere que as .so de IMAGEM são as do SHA256SUMS em acbr-libs/
# Rode depois do docker-build, ou contra uma imagem publicada:
# make imagem-libs-conferir IMAGEM=ghcr.io/4devsmart/wrapper-api/api:v1.3.1
imagem-libs-conferir:
@scripts/conferir-libs-imagem.sh "$(or $(IMAGEM),$(APP_IMAGE))" $(ACBRLIBS)

## docker-build: constrói a imagem (exige acbr-libs/)
docker-build: acbr-libs-conferir
docker build \
Expand All @@ -212,4 +218,4 @@ help:

.PHONY: build build-cgo run test test-cgo openapi openapi-check openapi-valida yaml-valida enums-conferir vet fmt fmt-check tidy limpar help \
acbr-fonte acbr-compilar acbr-extrair \
acbr-libs-baixar acbr-libs-publicar acbr-libs-conferir docker-build up down
acbr-libs-baixar acbr-libs-publicar acbr-libs-conferir imagem-libs-conferir docker-build up down
12 changes: 12 additions & 0 deletions docs/ACBRLIB.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,18 @@ arquivos entrarem em `acbr-libs/`: download truncado que chega ao diretório vir
`.so` corrompido embutido na imagem, e SIGSEGV em runtime longe da causa. Como o
repositório é público, o download não usa token.

O cache local vale só se bater com o `SHA256SUMS` **do release**. Comparar com o
`SHA256SUMS` local não basta: o `acbr-extrair` regera o local a partir do que
extraiu, e um cache com a lib oficial por cima das patchadas conferia consigo
mesmo e passava. Sem acesso ao release, o download avisa e segue com o cache.

A imagem também é conferida. A fumaça prova que a lib carrega, não qual lib é,
e a v1.3.0 saiu com a lib de NFS-e sem patch passando por ela. Agora o
`publicar.yml` e o `release.yml` comparam as `.so` dentro da imagem com o
`SHA256SUMS` do release (`scripts/conferir-libs-imagem.sh`), e o release não
promove a imagem se divergirem. As notas do release trazem o digest da imagem e
o sha da lib de NFS-e. Localmente: `make imagem-libs-conferir IMAGEM=<imagem>`.

### Num repositório novo, ou num fork

Os jobs `cgo` e `publicar` do CI baixam os `.so` do release apontado por
Expand Down
40 changes: 25 additions & 15 deletions scripts/baixar-acbr-libs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,6 @@ ARQUIVOS=(
)

mkdir -p "$DESTINO"

# Cache quente: se tudo já bate com o SHA256SUMS local, não baixa nada.
if [[ -f "$DESTINO/SHA256SUMS" ]]; then
if quebrados="$(cd "$DESTINO" && sha256sum -c --quiet SHA256SUMS 2>/dev/null)"; then
if [[ "$(tr -d '[:space:]' < "$DESTINO/revisao.txt" 2>/dev/null)" == "$REV" ]]; then
echo "acbr-libs: cache já íntegro para r${REV}: nada a baixar."
exit 0
fi
echo "acbr-libs: cache íntegro mas de outra revisão, rebaixando"
fi
echo "acbr-libs: cache inválido, rebaixando:"
sed 's/^/ · /' <<<"$quebrados"
fi

echo "acbr-libs: baixando r${REV} do release $TAG de $REPO"
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

Expand All @@ -53,6 +38,31 @@ baixar() {
fi
}

# Cache quente: o cache vale se bate com o SHA256SUMS DO RELEASE, não com o
# local. O local é regerado por `make acbr-extrair` a partir do que acabou de
# ser extraído, então sempre confere consigo mesmo: depois de extrair a lib
# oficial por cima das patchadas, este script dizia "nada a baixar" e o
# docker-build seguia com a lib sem patch.
if [[ -f "$DESTINO/SHA256SUMS" ]]; then
if curl -fsSL --retry 3 --retry-delay 2 -o "$tmp/SHA256SUMS" "$BASE/SHA256SUMS" &&
curl -fsSL --retry 3 --retry-delay 2 -o "$tmp/revisao.txt" "$BASE/revisao.txt"; then
if [[ "$(tr -d '[:space:]' < "$tmp/revisao.txt")" == "$REV" ]] &&
(cd "$DESTINO" && sha256sum -c --quiet "$tmp/SHA256SUMS" >/dev/null 2>&1); then
cp "$tmp/SHA256SUMS" "$tmp/revisao.txt" "$DESTINO"/
echo "acbr-libs: cache confere com o release $TAG (r${REV}): nada a baixar."
exit 0
fi
echo "acbr-libs: cache difere do release $TAG, rebaixando"
elif (cd "$DESTINO" && sha256sum -c --quiet SHA256SUMS >/dev/null 2>&1) &&
[[ "$(tr -d '[:space:]' < "$DESTINO/revisao.txt" 2>/dev/null)" == "$REV" ]]; then
# Sem rede não há com o que comparar. Seguir com o cache é o que permite
# trabalhar offline, mas sem fingir que ele foi conferido.
echo "AVISO: sem acesso ao release $TAG; usando o cache local SEM conferir contra ele." >&2
exit 0
fi
fi

echo "acbr-libs: baixando r${REV} do release $TAG de $REPO"
for f in "${ARQUIVOS[@]}"; do
echo " · $f"
baixar "$f"
Expand Down
47 changes: 47 additions & 0 deletions scripts/conferir-libs-imagem.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env bash
# Confere que as .so DENTRO de uma imagem são as do SHA256SUMS de um cache de
# libs (acbr-libs/, populado por `make acbr-libs-baixar` a partir do release em
# ACBR_LIBS_RELEASE).
#
# Existe porque a imagem v1.3.0 saiu com a lib de NFS-e oficial, sem os patches
# de docker/acbr-patches, enquanto o próprio release carregava as patchadas.
# Nada falhou: o Dockerfile copia o que estiver em acbr-libs/, e ninguém
# comparava o resultado. A fumaça prova que a lib carrega, não qual lib é.
#
# Uso: scripts/conferir-libs-imagem.sh <imagem> [diretório-do-cache]
set -euo pipefail

IMAGEM="${1:?uso: $0 <imagem> [diretório-do-cache]}"
CACHE="${2:-acbr-libs}"
SOS=(libacbrnfse64.so libacbrcte64.so libacbrmdfe64.so libacbrnfe64.so libacbrboleto64.so)

if [[ ! -f "$CACHE/SHA256SUMS" ]]; then
echo "ERRO: $CACHE/SHA256SUMS não existe. Rode 'make acbr-libs-baixar'." >&2
exit 1
fi

# --entrypoint direto no sha256sum: o entrypoint da imagem sobe a API ou o
# worker, e aqui só interessa ler os arquivos.
caminhos=("${SOS[@]/#//usr/lib/}")
na_imagem="$(docker run --rm --entrypoint sha256sum "$IMAGEM" "${caminhos[@]}")"

divergiu=0
for so in "${SOS[@]}"; do
esperado="$(awk -v f="$so" '$2 == f || $2 == "*"f {print $1}' "$CACHE/SHA256SUMS")"
obtido="$(awk -v f="/usr/lib/$so" '$2 == f {print $1}' <<<"$na_imagem")"
if [[ -z "$esperado" ]]; then
echo "ERRO: $so não está em $CACHE/SHA256SUMS" >&2
divergiu=1
elif [[ "$esperado" != "$obtido" ]]; then
echo "ERRO: $so na imagem é ${obtido:0:16}, o release declara ${esperado:0:16}" >&2
divergiu=1
else
echo " $so ${obtido:0:16}"
fi
done

if [[ "$divergiu" == 1 ]]; then
echo "ERRO: $IMAGEM não carrega as libs de $CACHE/SHA256SUMS." >&2
exit 1
fi
echo "libs da imagem conferem com $CACHE/SHA256SUMS"
Loading