main— released, signed images. Protected; PR-only.dev— integration branch. PRs targetdev;dev→maincuts a release.
- Branch from
dev:git checkout -b feat/my-change dev. - Commit with conventional-commit prefixes (
feat:,fix:,chore:,ci:). - Open a PR into
dev. CI must pass (hadolint, shellcheck, yamllint, actionlint, build, smoke test, Trivy). - Squash-merge.
docker buildx build --load -t ubuntu-desktop:dev .
IMAGE=ubuntu-desktop:dev ./tests/smoke.shTo test a slim variant:
docker buildx build --load \
--build-arg VARIANT=slim \
--build-arg INCLUDE_BROWSER=false \
--build-arg INCLUDE_MEDIA=false \
--build-arg INCLUDE_VSCODE=false \
-t ubuntu-desktop:dev-slim .Releases are fully automated by release-please driven by conventional commits.
- Merge conventional-commit PRs into
main(feat:,fix:,refactor:,feat!:for breaking, etc.). - release-please keeps an open "chore(main): release X.Y.Z" PR in sync,
bumping
VERSIONandCHANGELOG.mdaccording to commit semantics. - Merge the Release PR. release-please then creates the
vX.Y.Zgit tag and a GitHub release. - The
releaseworkflow fires on the tag, builds multi-arch (amd64,arm64), pushes to GHCR and Docker Hub (:vX.Y.Z,:X.Y,:X, plus:latestwhen releasing from default branch), generates SBOM + provenance, and signs digests with cosign (keyless OIDC).
To force a specific version (e.g. first stable), add a footer to any
commit on main:
Release-As: 1.0.0