Merge ci/reuse-images: tags and docs-only merges reuse the published images
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
7d010cfd7d
4 files changed
+80
-15
No files matched your search
@@ -18,8 +18,46 @@ env:
|
||||
REGISTRY: git.lucasrguerra.dev.br
|
||||
|
||||
jobs:
|
||||
# Nothing is published unless the full suite passes first.
|
||||
# The images are built from services/, migrations/ and config/ and nothing
|
||||
# else, so those three trees identify them. When an image for the same trees
|
||||
# is already in the registry (a docs-only merge, a workflow change, or a tag on
|
||||
# a commit that was just released), there is nothing to build: the new tags
|
||||
# are pointed at the existing manifest. This is what keeps tagging a release
|
||||
# from compiling the webmail a second time.
|
||||
plan:
|
||||
name: Plano (reaproveitar as imagens?)
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
inputs: ${{ steps.plan.outputs.inputs }}
|
||||
reuse: ${{ steps.plan.outputs.reuse }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- id: plan
|
||||
env:
|
||||
REGISTRY: ${{ env.REGISTRY }}
|
||||
REPO: ${{ gitea.repository }}
|
||||
run: |
|
||||
inputs=$(git rev-parse HEAD:services HEAD:migrations HEAD:config | sha256sum | cut -c1-16)
|
||||
ns="${REPO,,}"
|
||||
reuse=true
|
||||
for s in protocols auth storage webmail migrations rspamd; do
|
||||
token=$(curl -fsS "https://$REGISTRY/v2/token?service=container_registry&scope=repository:$ns/$s:pull" | jq -r .token)
|
||||
code=$(curl -s -o /dev/null -w '%{http_code}' -I \
|
||||
-H "Authorization: Bearer $token" \
|
||||
-H 'Accept: application/vnd.oci.image.index.v1+json,application/vnd.docker.distribution.manifest.list.v2+json' \
|
||||
"https://$REGISTRY/v2/$ns/$s/manifests/inputs-$inputs")
|
||||
echo "$s: inputs-$inputs -> HTTP $code"
|
||||
[ "$code" = 200 ] || reuse=false
|
||||
done
|
||||
echo "inputs=$inputs" >> "$GITHUB_OUTPUT"
|
||||
echo "reuse=$reuse" >> "$GITHUB_OUTPUT"
|
||||
echo "reuse the published images: $reuse"
|
||||
|
||||
# Nothing is published unless the full suite passes first. Skipped when the
|
||||
# images are reused: the same trees already went through it.
|
||||
ci:
|
||||
needs: plan
|
||||
if: ${{ needs.plan.outputs.reuse != 'true' }}
|
||||
uses: ./.gitea/workflows/ci.yml
|
||||
|
||||
# Registry paths must be lowercase, and this repository is "lucasrguerra/LambdaMail".
|
||||
@@ -42,7 +80,8 @@ jobs:
|
||||
# magnitude slower.
|
||||
build:
|
||||
name: Build ${{ matrix.service }} (${{ matrix.platform }})
|
||||
needs: [ci, prepare]
|
||||
needs: [plan, ci, prepare]
|
||||
if: ${{ needs.plan.outputs.reuse != 'true' }}
|
||||
runs-on: ${{ matrix.runner }}
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -104,7 +143,9 @@ jobs:
|
||||
|
||||
publish:
|
||||
name: Publica ${{ matrix.service }}
|
||||
needs: [build, prepare]
|
||||
needs: [plan, build, prepare]
|
||||
# build is skipped, not successful, when the images are reused.
|
||||
if: ${{ !cancelled() && needs.plan.result == 'success' && needs.prepare.result == 'success' && (needs.build.result == 'success' || needs.plan.outputs.reuse == 'true') }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -141,13 +182,20 @@ jobs:
|
||||
type=sha,format=long
|
||||
|
||||
# Joins both architectures under one manifest list, so `docker pull`
|
||||
# resolves to the right one automatically.
|
||||
- name: Cria o manifesto multi-arquitetura
|
||||
# resolves to the right one automatically. The inputs-<hash> tag is what
|
||||
# lets a later run recognise these images and skip the build.
|
||||
- name: Aplica as tags
|
||||
run: |
|
||||
image='${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}/${{ matrix.service }}'
|
||||
docker buildx imagetools create \
|
||||
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< '${{ steps.meta.outputs.json }}') \
|
||||
"$image:build-${{ gitea.sha }}-amd64" "$image:build-${{ gitea.sha }}-arm64"
|
||||
tags=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< '${{ steps.meta.outputs.json }}')
|
||||
inputs='${{ needs.plan.outputs.inputs }}'
|
||||
if [ '${{ needs.plan.outputs.reuse }}' = true ]; then
|
||||
# Same manifest under new names: no layer is read or written.
|
||||
docker buildx imagetools create $tags "$image:inputs-$inputs"
|
||||
else
|
||||
docker buildx imagetools create $tags -t "$image:inputs-$inputs" \
|
||||
"$image:build-${{ gitea.sha }}-amd64" "$image:build-${{ gitea.sha }}-arm64"
|
||||
fi
|
||||
|
||||
- name: Confere o resultado
|
||||
run: |
|
||||
|
||||
+3
-1
@@ -87,7 +87,9 @@ remain are listed in the README under
|
||||
`git.lucasrguerra.dev.br/lucasrguerra/lambdamail/<service>` and the `v*` tag
|
||||
creates a release with notes. See [docs/RELEASING.md](docs/RELEASING.md).
|
||||
- CI caches npm, Go modules, the embedded Postgres, Trivy's database and image
|
||||
layers.
|
||||
layers. Release recognises images it already published for the same sources
|
||||
(`inputs-<hash>` tag), so a documentation change or a version tag never
|
||||
rebuilds them.
|
||||
- One `.env.example` for development and production, and `scripts/init-env.sh`
|
||||
to create `.env` with random secrets (`make env`). It replaces
|
||||
`.env.production.example`.
|
||||
|
||||
+3
-1
@@ -87,7 +87,9 @@ Gitea. O que ainda falta está no README, em
|
||||
`git.lucasrguerra.dev.br/lucasrguerra/lambdamail/<serviço>`, e a tag `v*` cria
|
||||
uma release com notas. Veja [docs/RELEASING.md](docs/RELEASING.md).
|
||||
- O CI guarda em cache o npm, os módulos Go, o Postgres embutido, o banco do
|
||||
Trivy e as camadas das imagens.
|
||||
Trivy e as camadas das imagens. O Release reconhece imagens que já publicou
|
||||
para os mesmos fontes (tag `inputs-<hash>`), então uma mudança de documentação
|
||||
ou uma tag de versão nunca as reconstrói.
|
||||
- Um único `.env.example` para desenvolvimento e produção, e o
|
||||
`scripts/init-env.sh` para criar o `.env` com segredos aleatórios
|
||||
(`make env`). Ele substitui o `.env.production.example`.
|
||||
|
||||
+18
-5
@@ -11,10 +11,22 @@ the Oracle server.
|
||||
| Workflow | Runs on | Does |
|
||||
| :--- | :--- | :--- |
|
||||
| `ci.yml` | every push and pull request | language lint, Go and TypeScript suites against a real Postgres, webmail tests, a Trivy scan of the repository and of each image, and the whole stack brought up with compose |
|
||||
| `release.yml` | a push to `main`, a `v*` tag, or by hand | runs `ci.yml` again, builds the six images for `linux/amd64` and `linux/arm64`, publishes them, and on a tag creates the release |
|
||||
| `release.yml` | a push to `main`, a `v*` tag, or by hand | builds the six images for `linux/amd64` and `linux/arm64` after `ci.yml` passes, publishes them, and on a tag creates the release |
|
||||
|
||||
Nothing is published unless the full suite passes first.
|
||||
|
||||
### Images are reused, not rebuilt
|
||||
|
||||
The images are built from `services/`, `migrations/` and `config/` and nothing
|
||||
else. `release.yml` hashes those three trees (the `inputs-<hash>` tag) and asks
|
||||
the registry whether images for that hash already exist. If they do, it skips
|
||||
the CI run and the build and only points the new tags (`latest`, `sha-<commit>`,
|
||||
the version) at the existing manifests, which takes about a minute.
|
||||
|
||||
So these never recompile anything: a documentation or workflow change merged
|
||||
to `main`, and a version tag pushed on a commit whose images were just
|
||||
published. A change under the three trees rebuilds everything, as it should.
|
||||
|
||||
Images go to the Gitea registry, one package per service:
|
||||
|
||||
```
|
||||
@@ -39,10 +51,11 @@ logging in.
|
||||
git push gitea v1.2.3
|
||||
```
|
||||
|
||||
The tag triggers `release.yml` once more. It republishes the images under
|
||||
`1.2.3`, `1.2` and `1` (layers are cached, so this is quick) and creates the
|
||||
entry under **Releases** with the image list and the commit log since the
|
||||
previous tag. A tag with a hyphen (`v1.3.0-rc.1`) is marked as a pre-release.
|
||||
The tag triggers `release.yml` once more. Because the commit was already
|
||||
released from `main`, the images are reused: it adds the tags `1.2.3`, `1.2` and
|
||||
`1` to them without building, and creates the entry under **Releases** with the
|
||||
image list and the commit log since the previous tag. Tagging a commit whose
|
||||
`main` run has not finished (or never ran) builds it from scratch. A tag with a hyphen (`v1.3.0-rc.1`) is marked as a pre-release.
|
||||
|
||||
Versions follow [Semantic Versioning](https://semver.org/): a change to a
|
||||
migration, a required environment variable, or a published port is at least a
|
||||
|
||||
Reference in new issue
Block a user