Files
LambdaMail/.gitea/workflows/release.yml
T
Lucas RayanandClaude Sonnet 5.5 1aac8be531
CI / Lint de idioma (R9) (push) Successful in 4s
CI / TypeScript (auth) (push) Successful in 3m4s
CI / TypeScript (storage) (push) Successful in 14s
CI / Go (protocols) (push) Successful in 4m21s
CI / Trivy (dependências, segredos e Dockerfiles) (push) Successful in 11s
CI / Imagem (auth) (push) Successful in 10s
CI / Testes (webmail) (push) Successful in 52s
CI / Imagem (migrations) (push) Successful in 5s
CI / Imagem (protocols) (push) Successful in 6s
CI / Imagem (rspamd) (push) Successful in 5s
CI / Imagem (storage) (push) Successful in 8s
CI / Imagem (webmail) (push) Successful in 8s
CI / Stack completa (push) Failing after 4s
ci: create a Gitea release when a v* tag is pushed
The Release workflow only ever published container images; there was no entry
under Releases. The new job runs after every image is published and writes the
image list and the commit log since the previous tag.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 11:22:08 -03:00

208 lines
7.9 KiB
YAML

name: Release
# Publishes the service images to the Gitea container registry after a merge
# into main, and again under semver tags when a v* tag is pushed. The deploy
# host only pulls, so nothing has to be built on the server. Needs the user
# secret REGISTRY_TOKEN (a Gitea token with the write:package scope).
on:
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
concurrency:
group: release
cancel-in-progress: false
env:
REGISTRY: git.lucasrguerra.dev.br
jobs:
# Nothing is published unless the full suite passes first.
ci:
uses: ./.gitea/workflows/ci.yml
# Registry paths must be lowercase, and this repository is "lucasrguerra/LambdaMail".
# Lowercasing gitea.repository here keeps the workflow fork-safe instead of
# hardcoding the owner.
prepare:
name: Namespace das imagens
runs-on: ubuntu-latest
outputs:
namespace: ${{ steps.namespace.outputs.value }}
steps:
- id: namespace
# GITHUB_OUTPUT is the runner's own interface: Gitea sets it under that name.
run: |
repo='${{ gitea.repository }}'
echo "value=${repo,,}" >> "$GITHUB_OUTPUT"
# Each architecture is built on a runner of its own kind. QEMU emulation
# would also work, but it makes the Go and Next builds roughly an order of
# magnitude slower.
build:
name: Build ${{ matrix.service }} (${{ matrix.platform }})
needs: [ci, prepare]
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
# Both are axes, so this is a cross product: four services on two
# architectures is eight jobs. Declaring platform only under `include`
# would not multiply anything - include can add keys to existing
# combinations, but it cannot create the combinations themselves.
service: [protocols, auth, storage, webmail, migrations, rspamd]
platform: [linux/amd64, linux/arm64]
include:
- platform: linux/amd64
runner: linux-amd64
arch: amd64
- platform: linux/arm64
runner: ubuntu-24.04-arm
arch: arm64
steps:
- uses: actions/checkout@v7
# The only runner is arm64; linux/amd64 is built under QEMU emulation
# (label linux-amd64 points at the same runner), so it is slower.
- uses: docker/setup-qemu-action@v3
if: matrix.arch == 'amd64'
- uses: docker/setup-buildx-action@v4
- name: Autentica no registro
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ gitea.actor }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Metadados
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}/${{ matrix.service }}
# Each architecture is pushed under its own tag (no digest artifacts: they
# are not portable to Gitea). The final tags are attached later, once both
# exist and can be joined into a single manifest list.
- name: Build e push por arquitetura
id: build
uses: docker/build-push-action@v7
with:
context: ${{ (matrix.service == 'migrations' || matrix.service == 'rspamd') && '.' || format('services/{0}', matrix.service) }}
file: services/${{ matrix.service }}/Dockerfile
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
push: true
tags: ${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}/${{ matrix.service }}:build-${{ gitea.sha }}-${{ matrix.arch }}
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}/${{ matrix.service }}:buildcache-${{ matrix.arch }}
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}/${{ matrix.service }}:buildcache-${{ matrix.arch }},mode=max
publish:
name: Publica ${{ matrix.service }}
needs: [build, prepare]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
service: [protocols, auth, storage, webmail, migrations, rspamd]
steps:
- uses: actions/checkout@v7
- uses: docker/setup-buildx-action@v4
- name: Autentica no registro
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ gitea.actor }}
password: ${{ secrets.REGISTRY_TOKEN }}
# `latest` is what compose pulls. The semver tags only materialise on a
# v* tag push and make it possible to pin or roll back to an exact
# release; the sha tag does the same for any main build.
- name: Metadados
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}/${{ matrix.service }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest
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
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"
- name: Confere o resultado
run: |
docker buildx imagetools inspect \
"${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}/${{ matrix.service }}:latest" \
--format '{{json .Manifest}}' \
| jq -r '.manifests[] | select(.platform.os == "linux") | "\(.platform.os)/\(.platform.architecture)"'
- name: Resumo
run: |
{
echo "### ${{ matrix.service }} publicado (linux/amd64 e linux/arm64)"
echo
echo '```'
echo "${{ steps.meta.outputs.tags }}"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
# Cria a entrada em "Releases" do repositório. Só roda quando uma tag v* é
# enviada, depois que todas as imagens foram publicadas. Usa o token do próprio
# Actions, então não precisa de segredo extra.
gitea-release:
name: Cria a release no Gitea
needs: [publish, prepare]
if: startsWith(gitea.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Release
env:
TAG: ${{ gitea.ref_name }}
TOKEN: ${{ gitea.token }}
API: ${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}
NAMESPACE: ${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}
run: |
prev=$(git describe --tags --abbrev=0 "$TAG^" 2>/dev/null || true)
range=${prev:+$prev..}$TAG
{
echo "## Imagens"
echo
echo '```'
for s in protocols auth storage webmail migrations rspamd; do
echo "$NAMESPACE/$s:${TAG#v}"
done
echo '```'
echo
echo "## Mudanças${prev:+ desde $prev}"
echo
git log --no-merges --format='- %s (%h)' "$range"
} > notes.md
pre=false; case "$TAG" in *-*) pre=true ;; esac
jq -n --arg tag "$TAG" --arg name "LambdaMail ${TAG#v}" --rawfile body notes.md --argjson pre "$pre" \
'{tag_name:$tag, name:$name, body:$body, prerelease:$pre}' > release.json
curl -fsS -X POST "$API/releases" -H "Authorization: token $TOKEN" \
-H 'Content-Type: application/json' -d @release.json