Compare commits

...
5 Commits
Author SHA1 Message Date
Lucas RayanandClaude Sonnet 5.5 448bb59672 chore(wolf): record the CI, branch and release conventions
Release / Plano (reaproveitar as imagens?) (push) Successful in 4s
Release / ci (push) Skipped
Release / Build auth (linux/amd64) (push) Skipped
Release / Build migrations (linux/amd64) (push) Skipped
Release / Build protocols (linux/amd64) (push) Skipped
Release / Build rspamd (linux/amd64) (push) Skipped
Release / Build storage (linux/amd64) (push) Skipped
Release / Build webmail (linux/amd64) (push) Skipped
Release / Build auth (linux/arm64) (push) Skipped
Release / Build migrations (linux/arm64) (push) Skipped
Release / Build protocols (linux/arm64) (push) Skipped
Release / Build rspamd (linux/arm64) (push) Skipped
Release / Build storage (linux/arm64) (push) Skipped
Release / Build webmail (linux/arm64) (push) Skipped
Release / Namespace das imagens (push) Successful in 0s
Release / Publica auth (push) Successful in 17s
Release / Publica migrations (push) Successful in 19s
Release / Publica protocols (push) Successful in 17s
Release / Publica rspamd (push) Successful in 20s
Release / Publica storage (push) Successful in 21s
Release / Publica webmail (push) Successful in 16s
Release / Cria a release no Gitea (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 12:43:52 -03:00
Lucas RayanandClaude Sonnet 5.5 a6719c6018 ci: fix the release job being skipped, and take the notes from the changelog
Release / Plano (reaproveitar as imagens?) (push) Successful in 4s
Release / ci (push) Skipped
Release / Namespace das imagens (push) Successful in 1s
Release / Build auth (linux/amd64) (push) Skipped
Release / Build migrations (linux/amd64) (push) Skipped
Release / Build protocols (linux/amd64) (push) Skipped
Release / Build rspamd (linux/amd64) (push) Skipped
Release / Build storage (linux/amd64) (push) Skipped
Release / Build webmail (linux/amd64) (push) Skipped
Release / Build auth (linux/arm64) (push) Skipped
Release / Build migrations (linux/arm64) (push) Skipped
Release / Build protocols (linux/arm64) (push) Skipped
Release / Build rspamd (linux/arm64) (push) Skipped
Release / Build storage (linux/arm64) (push) Skipped
Release / Build webmail (linux/arm64) (push) Skipped
Release / Publica migrations (push) Successful in 20s
Release / Publica auth (push) Successful in 20s
Release / Publica protocols (push) Successful in 20s
Release / Publica rspamd (push) Successful in 20s
Release / Publica storage (push) Successful in 21s
Release / Publica webmail (push) Successful in 21s
Release / Cria a release no Gitea (push) Skipped
The job inherited the implicit success() and was skipped whenever the build job
upstream was skipped (images reused). It now checks publish explicitly, like
publish does. Notes come from the matching CHANGELOG section through
scripts/release-notes.sh, not from the commit log of the whole history.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 12:27:08 -03:00
Lucas RayanandClaude Sonnet 5.5 1bcbe4d774 Merge ci/reuse-images: tags and docs-only merges reuse the published images
Release / Namespace das imagens (push) Successful in 1s
Release / Plano (reaproveitar as imagens?) (push) Successful in 4s
Release / ci (push) Skipped
Release / Build auth (linux/amd64) (push) Skipped
Release / Build migrations (linux/amd64) (push) Skipped
Release / Build protocols (linux/amd64) (push) Skipped
Release / Build rspamd (linux/amd64) (push) Skipped
Release / Build storage (linux/amd64) (push) Skipped
Release / Build webmail (linux/amd64) (push) Skipped
Release / Build auth (linux/arm64) (push) Skipped
Release / Build migrations (linux/arm64) (push) Skipped
Release / Build protocols (linux/arm64) (push) Skipped
Release / Build rspamd (linux/arm64) (push) Skipped
Release / Build storage (linux/arm64) (push) Skipped
Release / Build webmail (linux/arm64) (push) Skipped
Release / Publica auth (push) Successful in 20s
Release / Publica migrations (push) Successful in 20s
Release / Publica rspamd (push) Successful in 21s
Release / Publica protocols (push) Successful in 22s
Release / Publica webmail (push) Successful in 20s
Release / Publica storage (push) Successful in 21s
Release / Cria a release no Gitea (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 12:20:51 -03:00
Lucas RayanandClaude Sonnet 5.5 a3e071bb03 ci: reuse published images when the image sources are unchanged
Release hashes services/, migrations/ and config/; when images for that hash
already exist it skips CI and the build and only applies the new tags. Tagging
a release no longer recompiles the webmail.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 12:20:51 -03:00
Lucas RayanandClaude Sonnet 5.5 2db5915061 Merge release/v1.0.0: changelog, one .env.example, release docs, faster CI, Gitea release job
CI / Lint de idioma (R9) (push) Successful in 3s
CI / Stack completa (push) Canceled after 0s
CI / Imagem (webmail) (push) Canceled after 0s
CI / Imagem (storage) (push) Canceled after 0s
CI / Imagem (rspamd) (push) Canceled after 0s
CI / Imagem (protocols) (push) Canceled after 0s
CI / Imagem (migrations) (push) Canceled after 0s
CI / Imagem (auth) (push) Canceled after 0s
CI / Trivy (dependências, segredos e Dockerfiles) (push) Canceled after 0s
CI / Testes (webmail) (push) Canceled after 0s
CI / TypeScript (storage) (push) Canceled after 0s
CI / TypeScript (auth) (push) Canceled after 2m6s
CI / Go (protocols) (push) Canceled after 2m8s
Release / ci (push) Canceled after 2m9s
Release / Namespace das imagens (push) Canceled after 0s
Release / Build auth (linux/amd64) (push) Canceled after 0s
Release / Build auth (linux/arm64) (push) Canceled after 0s
Release / Build migrations (linux/amd64) (push) Canceled after 0s
Release / Build migrations (linux/arm64) (push) Canceled after 0s
Release / Build protocols (linux/amd64) (push) Canceled after 0s
Release / Build protocols (linux/arm64) (push) Canceled after 0s
Release / Build rspamd (linux/amd64) (push) Canceled after 0s
Release / Build rspamd (linux/arm64) (push) Canceled after 0s
Release / Build storage (linux/amd64) (push) Canceled after 0s
Release / Build storage (linux/arm64) (push) Canceled after 0s
Release / Build webmail (linux/amd64) (push) Canceled after 0s
Release / Build webmail (linux/arm64) (push) Canceled after 0s
Release / Publica auth (push) Canceled after 0s
Release / Publica migrations (push) Canceled after 0s
Release / Publica protocols (push) Canceled after 0s
Release / Publica rspamd (push) Canceled after 0s
Release / Publica storage (push) Canceled after 0s
Release / Publica webmail (push) Canceled after 0s
Release / Cria a release no Gitea (push) Canceled after 0s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 11:44:19 -03:00
6 changed files with 132 additions and 31 deletions

No files matched your search

+59 -23
View File
@@ -18,8 +18,46 @@ env:
REGISTRY: git.lucasrguerra.dev.br REGISTRY: git.lucasrguerra.dev.br
jobs: 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: ci:
needs: plan
if: ${{ needs.plan.outputs.reuse != 'true' }}
uses: ./.gitea/workflows/ci.yml uses: ./.gitea/workflows/ci.yml
# Registry paths must be lowercase, and this repository is "lucasrguerra/LambdaMail". # Registry paths must be lowercase, and this repository is "lucasrguerra/LambdaMail".
@@ -42,7 +80,8 @@ jobs:
# magnitude slower. # magnitude slower.
build: build:
name: Build ${{ matrix.service }} (${{ matrix.platform }}) name: Build ${{ matrix.service }} (${{ matrix.platform }})
needs: [ci, prepare] needs: [plan, ci, prepare]
if: ${{ needs.plan.outputs.reuse != 'true' }}
runs-on: ${{ matrix.runner }} runs-on: ${{ matrix.runner }}
permissions: permissions:
contents: read contents: read
@@ -104,7 +143,9 @@ jobs:
publish: publish:
name: Publica ${{ matrix.service }} 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 runs-on: ubuntu-latest
permissions: permissions:
contents: read contents: read
@@ -141,13 +182,20 @@ jobs:
type=sha,format=long type=sha,format=long
# Joins both architectures under one manifest list, so `docker pull` # Joins both architectures under one manifest list, so `docker pull`
# resolves to the right one automatically. # resolves to the right one automatically. The inputs-<hash> tag is what
- name: Cria o manifesto multi-arquitetura # lets a later run recognise these images and skip the build.
- name: Aplica as tags
run: | run: |
image='${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}/${{ matrix.service }}' image='${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}/${{ matrix.service }}'
docker buildx imagetools create \ tags=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< '${{ steps.meta.outputs.json }}')
$(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" "$image:build-${{ gitea.sha }}-amd64" "$image:build-${{ gitea.sha }}-arm64"
fi
- name: Confere o resultado - name: Confere o resultado
run: | run: |
@@ -172,7 +220,9 @@ jobs:
gitea-release: gitea-release:
name: Cria a release no Gitea name: Cria a release no Gitea
needs: [publish, prepare] needs: [publish, prepare]
if: startsWith(gitea.ref, 'refs/tags/v') # Explicit, like publish: the build job upstream is skipped when the images
# are reused, and the implicit success() would then skip this job too.
if: ${{ !cancelled() && needs.publish.result == 'success' && startsWith(gitea.ref, 'refs/tags/v') }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
@@ -185,21 +235,7 @@ jobs:
API: ${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }} API: ${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}
NAMESPACE: ${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }} NAMESPACE: ${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}
run: | run: |
prev=$(git describe --tags --abbrev=0 "$TAG^" 2>/dev/null || true) scripts/release-notes.sh "$TAG" "$NAMESPACE" > notes.md
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 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" \ 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 '{tag_name:$tag, name:$name, body:$body, prerelease:$pre}' > release.json
+10 -1
View File
@@ -6,18 +6,23 @@ budget_tokens: 2000
> OpenWolf's learning memory. Updated automatically as the AI learns from interactions. > OpenWolf's learning memory. Updated automatically as the AI learns from interactions.
> Do not edit manually unless correcting an error. > Do not edit manually unless correcting an error.
> Last updated: 2026-10-06 > Last updated: 2026-10-30
## User Preferences ## User Preferences
<!-- How the user likes things done. Code style, tools, patterns, communication. --> <!-- How the user likes things done. Code style, tools, patterns, communication. -->
- Writes in Portuguese (pt-BR); answer in Portuguese. Wants things finished end to end: merged, released, deployed and checked in production, without being asked again (said so on 2026-10-22). - Writes in Portuguese (pt-BR); answer in Portuguese. Wants things finished end to end: merged, released, deployed and checked in production, without being asked again (said so on 2026-10-22).
- Likes a clean interface with every function reachable (Apple / Anthropic as references, Outlook over Gmail), dark theme by default with a light one. - Likes a clean interface with every function reachable (Apple / Anthropic as references, Outlook over Gmail), dark theme by default with a light one.
- [2026-10-30] CI must be efficient: never redo work already done for the same content. Hash what goes into the artifact (the `inputs-<hash>` image tag, or a filtered `git ls-tree` hash), ask the registry, and skip CI and build when it exists; a version tag or a docs/workflow-only merge only re-points tags. Do not run `ci.yml` on `main` by push when `release.yml` already calls it. Use `paths-ignore` for docs and tool state (`.wolf`, `.claude`, `.superset`). The user repeated this several times and was angry when a tag was about to recompile the Next.js build.
- [2026-10-30] Keep repositories clean: after a branch is merged, delete it on Gitea and locally (`git push gitea --delete <b>`, `git branch -d <b>`). Never touch the user's own unmerged branches.
- [2026-10-30] Every tool gets semver tags and a Gitea Release (Releases tab, not just container images) with notes from the matching `CHANGELOG.md` section (`scripts/release-notes.sh`). Keep the changelog updated under Unreleased. A tag job that creates the release must use an explicit `if: ${{ !cancelled() && needs.<job>.result == 'success' && startsWith(gitea.ref, 'refs/tags/v') }}`.
- [2026-10-30] Source of truth is Gitea (`git.lucasrguerra.dev.br`, remote `gitea`, SSH on port 22 through the host). The GitHub account is suspended (404): never push to or depend on GitHub. Workflows live in `.gitea/workflows` and use the `gitea.*` context.
## Key Learnings ## Key Learnings
- **Project:** LambdaMail - **Project:** LambdaMail
- **Description:** A self-hosted mail server with an integrated webmail and admin console, built to - **Description:** A self-hosted mail server with an integrated webmail and admin console, built to
- [2026-10-30] Release flow: `plan` job hashes `services/ migrations/ config/` → `inputs-<hash>`; reuse takes ~1 min instead of 30. A job downstream of a skipped `build` needs explicit status checks in its `if`. Images: `git.lucasrguerra.dev.br/lucasrguerra/lambdamail/<service>`. v1.0.0 was the first tag (no history survived from GitHub). Production deploy is blocked until the Coolify app's source is switched from Public GitHub to Gitea in the Coolify UI (API cannot); deploy only when the user is assured everything is right. Single `.env.example` + `scripts/init-env.sh` (`make env`).
## Do-Not-Repeat ## Do-Not-Repeat
@@ -31,6 +36,10 @@ budget_tokens: 2000
- [2026-10-22] A `<Button>` inside a `<form>` submits it unless `type="button"` (Undo send used to resend the message). - [2026-10-22] A `<Button>` inside a `<form>` submits it unless `type="button"` (Undo send used to resend the message).
- [2026-10-22] Never run side effects inside a React state updater (it can run twice). - [2026-10-22] Never run side effects inside a React state updater (it can run twice).
- [2026-10-23] Inside a JS template literal `\\d` silently becomes `d`. For scripts embedded in template strings write `[0-9]` instead of `\\d`, and test the script's behaviour, not only that it is present. - [2026-10-23] Inside a JS template literal `\\d` silently becomes `d`. For scripts embedded in template strings write `[0-9]` instead of `\\d`, and test the script's behaviour, not only that it is present.
- [2026-10-30] On the Gitea runners never mount the job workspace into a container with `-v "$PWD:..."`: the Docker daemon (DinD) cannot see it and mounts an empty directory. Use `docker create` + `docker cp` + `docker start -a`.
- [2026-10-30] Do not use `aquasecurity/trivy-action` (clones Trivy from the Gitea server), `actions/upload-artifact` v4+, the `gha` buildx cache or `rustsec/audit-check`. Run Trivy from `aquasec/trivy` with `-v trivy-cache:/root/.cache/`, use the registry for cache, `cargo audit` for Rust. The runner sets `GITHUB_OUTPUT`/`GITHUB_STEP_SUMMARY` but NOT `GITEA_OUTPUT` etc.
- [2026-10-30] Pushing old tags to Gitea triggers the `.github/workflows/release.yml` found in those old commits (Gitea falls back to `.github` where `.gitea` is absent). They queue forever on missing macOS/Windows runners: cancel them (`POST /repos/{o}/{r}/actions/runs/{id}/cancel`) right after pushing tags.
- [2026-10-30] The runner Docker daemons persist between jobs: steps that create networks/containers must be idempotent and clean up with `if: always()`.
## Decision Log ## Decision Log
+3 -1
View File
@@ -87,7 +87,9 @@ remain are listed in the README under
`git.lucasrguerra.dev.br/lucasrguerra/lambdamail/<service>` and the `v*` tag `git.lucasrguerra.dev.br/lucasrguerra/lambdamail/<service>` and the `v*` tag
creates a release with notes. See [docs/RELEASING.md](docs/RELEASING.md). 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 - 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` - One `.env.example` for development and production, and `scripts/init-env.sh`
to create `.env` with random secrets (`make env`). It replaces to create `.env` with random secrets (`make env`). It replaces
`.env.production.example`. `.env.production.example`.
+3 -1
View File
@@ -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 `git.lucasrguerra.dev.br/lucasrguerra/lambdamail/<serviço>`, e a tag `v*` cria
uma release com notas. Veja [docs/RELEASING.md](docs/RELEASING.md). 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 - 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 - Um único `.env.example` para desenvolvimento e produção, e o
`scripts/init-env.sh` para criar o `.env` com segredos aleatórios `scripts/init-env.sh` para criar o `.env` com segredos aleatórios
(`make env`). Ele substitui o `.env.production.example`. (`make env`). Ele substitui o `.env.production.example`.
+18 -5
View File
@@ -11,10 +11,22 @@ the Oracle server.
| Workflow | Runs on | Does | | 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 | | `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. 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: Images go to the Gitea registry, one package per service:
``` ```
@@ -39,10 +51,11 @@ logging in.
git push gitea v1.2.3 git push gitea v1.2.3
``` ```
The tag triggers `release.yml` once more. It republishes the images under The tag triggers `release.yml` once more. Because the commit was already
`1.2.3`, `1.2` and `1` (layers are cached, so this is quick) and creates the released from `main`, the images are reused: it adds the tags `1.2.3`, `1.2` and
entry under **Releases** with the image list and the commit log since the `1` to them without building, and creates the entry under **Releases** with the
previous tag. A tag with a hyphen (`v1.3.0-rc.1`) is marked as a pre-release. 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 Versions follow [Semantic Versioning](https://semver.org/): a change to a
migration, a required environment variable, or a published port is at least a migration, a required environment variable, or a published port is at least a
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
# Prints the release notes for a tag: the images it publishes, then the matching
# section of CHANGELOG.md (the commit log since the previous tag if the changelog
# has no section for that version).
#
# scripts/release-notes.sh v1.2.3 git.example.com/owner/project
set -euo pipefail
tag=${1:?usage: release-notes.sh <tag> <image-namespace>}
namespace=${2:?usage: release-notes.sh <tag> <image-namespace>}
version=${tag#v}
echo "## Images"
echo
echo '```'
for service in protocols auth storage webmail migrations rspamd; do
echo "$namespace/$service:$version"
done
echo '```'
echo
section=""
if [ -f CHANGELOG.md ]; then
section=$(awk -v head="## [$version]" '
index($0, head) == 1 { found = 1; next }
found && /^## \[/ { exit }
found && /^\[[^]]+\]: / { next }
found { print }
' CHANGELOG.md)
fi
if [ -n "$(printf '%s' "$section" | tr -d '[:space:]')" ]; then
printf '%s\n' "$section"
else
previous=$(git describe --tags --abbrev=0 "$tag^" 2>/dev/null || true)
echo "## Changes${previous:+ since $previous}"
echo
git log --no-merges --format='- %s (%h)' "${previous:+$previous..}$tag"
fi