- CHANGELOG.md and CHANGELOG.pt-BR.md, from the project history. - .env.example now serves development and production; scripts/init-env.sh (make env) creates .env with random secrets. .env.production.example is gone. - docs/RELEASING.md: the pipeline, how a version is cut and deployed, the runners. - DEPLOYMENT.md and both READMEs updated for Gitea and the new env flow. - Service versions set to 1.0.0. - CI: the proxy network step is idempotent and the stack is torn down always, since the runner's Docker daemon now outlives a job. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
3.8 KiB
Releasing LambdaMail
How a version gets from a commit to a server, and what runs where. The history of what changed is in the CHANGELOG.
The pipeline
Everything runs on Gitea Actions (.gitea/workflows/), on runners hosted on
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 |
Nothing is published unless the full suite passes first.
Images go to the Gitea registry, one package per service:
git.lucasrguerra.dev.br/lucasrguerra/lambdamail/{protocols,auth,storage,webmail,migrations,rspamd}
Tags on every image: latest, sha-<full commit>, and on a version tag also
1.2.3, 1.2 and 1. The packages are public, so a server pulls them without
logging in.
Cutting a release
-
Update CHANGELOG.md and CHANGELOG.pt-BR.md: move what is under Unreleased to a new
## [X.Y.Z] - datesection. -
Set
"version"inservices/{auth,storage,webmail}/package.jsonand their lockfiles to the same number. -
Merge to
mainand wait for theReleaserun of that merge to go green. -
Tag the merge commit and push the tag:
git tag -a v1.2.3 -m "LambdaMail 1.2.3" 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.
Versions follow Semantic Versioning: a change to a migration, a required environment variable, or a published port is at least a minor release, and anything that needs manual steps on upgrade is called out in the changelog.
Deploying a version
Coolify pulls; it does not build. See the Upgrading section of
DEPLOYMENT.md for the two-step deploy (set IMAGE_TAG, then
deploy). In short:
IMAGE_TAG= the release (1.2.3) orsha-<commit>. Neverlatest.- Deploy. Migrations run before the services that need them.
- Check
GET /healthonprotocolsand sign in to the webmail.
To roll back, set IMAGE_TAG back to the previous release and deploy again.
Migrations are not reverted: write them so the previous version still runs
against the new schema.
Runners
Two act_runner instances run on the Oracle server (Ampere ARM, 4 cores), each
with its own Docker-in-Docker daemon, so two jobs never share a network or a
port. The jobs run unprivileged.
linux/arm64images build natively;linux/amd64images build under QEMU emulation, which is slower. Layers are cached in the registry.- The Docker daemon of each runner keeps its layer cache between runs, and the toolcache volume keeps downloaded toolchains.
- The workspace of a job is not visible to the Docker daemon, so workflows copy
files into containers with
docker cpinstead of mounting them with-v. - Windows and macOS jobs cannot run here.
Why some actions are not used
aquasecurity/trivy-actionclones Trivy from the workflow's own server, which is Gitea; Trivy runs from its official image instead.actions/upload-artifactv4+ and theghabuild cache are GitHub services; per-architecture images are told apart by tag, and the cache lives in the registry.- Workflows read
GITHUB_OUTPUTandGITHUB_STEP_SUMMARYunder those names: theGITEA_*equivalents are not set by the runner.