Adopt commit-based container tags (#232)
## Summary - Replace git-tag-triggered container builds with path-based triggers on main and workflow_dispatch - Image tags now encode upstream app version + commit SHA (`vX.Y.Z-<sha>`) for full traceability - Replace `container-tag-and-release` task with `container-build-and-release` (dispatches workflows via Forgejo API) - Update dagger `publish()` to accept `commit_sha` parameter - Update all docs and references to the new workflow ## Deployment and Testing - [ ] Merge to main - [ ] `mise run container-build-and-release <name>` for each container to populate new-format tags - [ ] Verify tags in registry via `mise run container-list` - [ ] Existing images untouched — old tags remain available Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/232
This commit is contained in:
parent
0e2c10176d
commit
ffa8727660
13 changed files with 363 additions and 258 deletions
1
docs/changelog.d/harden-zot-registry.feature.md
Normal file
1
docs/changelog.d/harden-zot-registry.feature.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Container builds now trigger automatically on merge to main (path-based) and use commit-SHA-based image tags (`vX.Y.Z-<sha>`) for full traceability. The `container-tag-and-release` task is replaced by `container-build-and-release` which dispatches workflows via the Forgejo API. Added pre-commit hook to keep container versions in sync with `service-versions.yaml`.
|
||||
|
|
@ -71,16 +71,14 @@ When an attempt fails and you discover prerequisites, the branch must be cleaned
|
|||
|
||||
The branch between attempts should contain only documentation. Code returns when prerequisites are satisfied and the next attempt succeeds.
|
||||
|
||||
### Build artifacts and tags
|
||||
### Build artifacts
|
||||
|
||||
Mikado resets apply to branch code, not build artifacts. Container images in the registry and git tags created by `container-tag-and-release` are independent of branch lifecycle:
|
||||
Mikado resets apply to branch code, not build artifacts. Container images in the registry are independent of branch lifecycle:
|
||||
|
||||
- **Git tags** point to commit SHAs, not branches — they survive branch deletion and force-pushes.
|
||||
- **Registry images** are build outputs cached in zot — a wrong image is overwritten by the next release.
|
||||
- **If a build succeeds but deployment fails**, the image is fine; the problem is elsewhere. Document what you learned, bump the version, and try again.
|
||||
- **If a build fails in CI**, no image is pushed. Delete the git tag (`git tag -d <tag> && git push --delete origin <tag>`) and fix the nix/dockerfile before re-releasing.
|
||||
|
||||
Tag freely during leaf node work. The build IS the verification step — deferring it creates a chicken-and-egg where the card can't be marked complete without a built image.
|
||||
- **Registry images** are build outputs cached in zot — tagged with commit SHAs, so each build is unique and traceable.
|
||||
- **Automatic builds** trigger when container changes merge to main. Use `mise run container-build-and-release` for manual dispatch.
|
||||
- **If a build succeeds but deployment fails**, the image is fine; the problem is elsewhere. Document what you learned and try again.
|
||||
- **If a build fails in CI**, no image is pushed. Fix the nix/dockerfile and re-merge or re-dispatch.
|
||||
|
||||
## Card Conventions
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ Discovered while attempting [[deploy-authentik]]: the deployment references `reg
|
|||
|
||||
1. Verify `containers/authentik/default.nix` builds — locally via Dagger (`dagger call build-nix --src=. --container-name=authentik`) or on ringtail (the CI nix builder runs there)
|
||||
2. The `ak` entrypoint needs bash (included via `bashInteractive`) and orchestrates both `server` and `worker` subcommands
|
||||
3. Tag and release: `mise run container-tag-and-release authentik v1.0.0`
|
||||
3. Trigger build: `mise run container-build-and-release authentik`
|
||||
4. Verify the `-nix` tagged image appears in the registry
|
||||
|
||||
## What We Learned
|
||||
|
|
|
|||
|
|
@ -52,20 +52,23 @@ nix-build containers/<name>/default.nix -o result
|
|||
|
||||
## 3. Release
|
||||
|
||||
Once the image builds cleanly, create a tagged release:
|
||||
Container builds trigger automatically when changes to `containers/<name>/` are merged to `main`. Both workflows fire and each skips if the relevant build file is absent.
|
||||
|
||||
To trigger a manual build (e.g. from a branch or to rebuild at a specific commit):
|
||||
|
||||
```bash
|
||||
mise run container-tag-and-release <name> v1.0.0
|
||||
mise run container-build-and-release <name>
|
||||
mise run container-build-and-release <name> --ref <commit-sha>
|
||||
```
|
||||
|
||||
Use `--dry-run` to preview without creating tags.
|
||||
|
||||
This creates a single git tag `<name>-v1.0.0` and pushes it. Both Forgejo workflows trigger on the tag — each checks for its build file and skips if not present:
|
||||
Use `--dry-run` to preview without dispatching.
|
||||
|
||||
| Build file | Workflow | Runner | Registry tag |
|
||||
|------------|----------|--------|--------------|
|
||||
| `Dockerfile` | `build-container.yaml` | `k8s` (indri) | `:v1.0.0` |
|
||||
| `default.nix` | `build-container-nix.yaml` | `nix-container-builder` ([[ringtail]]) | `:v1.0.0-nix` |
|
||||
| `Dockerfile` | `build-container.yaml` | `k8s` (indri) | `:vX.Y.Z-<sha>` |
|
||||
| `default.nix` | `build-container-nix.yaml` | `nix-container-builder` ([[ringtail]]) | `:vX.Y.Z-<sha>-nix` |
|
||||
|
||||
The version (`X.Y.Z`) is extracted from `ARG CONTAINER_APP_VERSION=` in the Dockerfile or `version = "..."` in `default.nix`. The SHA is the short (7-char) commit hash.
|
||||
|
||||
Check available images and tags with:
|
||||
|
||||
|
|
@ -78,7 +81,7 @@ mise run container-list
|
|||
Change the image reference in `argocd/manifests/<service>/deployment.yaml`:
|
||||
|
||||
```yaml
|
||||
image: registry.ops.eblu.me/blumeops/<name>:v1.0.0
|
||||
image: registry.ops.eblu.me/blumeops/<name>:vX.Y.Z-abc1234
|
||||
```
|
||||
|
||||
Then deploy per [[deploy-k8s-service]].
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
title: Adopt Commit-Based Container Tags
|
||||
modified: 2026-02-20
|
||||
status: active
|
||||
requires:
|
||||
- add-container-version-sync-check
|
||||
tags:
|
||||
|
|
@ -29,9 +28,9 @@ Currently, container builds trigger on git tags matching `<container>-vX.Y.Z`. T
|
|||
### Triggers
|
||||
|
||||
1. **Merged changes to main** — any push to `main` that modifies files under `containers/<name>/` triggers builds for that container
|
||||
2. **Manual workflow dispatch** — for ad-hoc builds (e.g., testing on a branch). Accepts two inputs:
|
||||
2. **Manual workflow dispatch** — for ad-hoc builds. Accepts two inputs:
|
||||
- `container` (required) — which container to build
|
||||
- `ref` (optional, string) — the source commit SHA to build, defaulting to `HEAD` of `main`
|
||||
- `ref` (optional, string) — the source commit SHA to build, defaulting to `GITHUB_SHA`
|
||||
|
||||
Both the Dockerfile and Nix workflows fire for each trigger, each bailing out if the container lacks the relevant build file (same as today).
|
||||
|
||||
|
|
@ -40,56 +39,43 @@ Both the Dockerfile and Nix workflows fire for each trigger, each bailing out if
|
|||
Each container's version is extracted at build time from existing declarations — no separate VERSION file:
|
||||
|
||||
- **Dockerfile builds**: parsed from `ARG CONTAINER_APP_VERSION=<value>` in the Dockerfile
|
||||
- **Nix builds**: extracted via `dagger call nix-version` or `nix eval`
|
||||
- **Nix builds**: extracted from `version = "..."` in `default.nix`, or `CONTAINER_APP_VERSION` from the Dockerfile, or `dagger call nix-version` for nixpkgs packages
|
||||
|
||||
The [[add-container-version-sync-check]] pre-commit check ensures these declarations stay in sync with `service-versions.yaml`. See [[pin-container-versions]] for the work to ensure every container has a parseable version.
|
||||
|
||||
### Image Tag Format
|
||||
|
||||
The registry image tag encodes the app version and the exact source commit:
|
||||
|
||||
| Scenario | Dockerfile tag | Nix tag |
|
||||
|----------|---------------|---------|
|
||||
| Main branch build | `vX.Y.Z-<sha>` and `vX.Y.Z-<sha>-main` | `vX.Y.Z-<sha>-nix` and `vX.Y.Z-<sha>-main-nix` |
|
||||
| Manual dispatch | `vX.Y.Z-<sha>` | `vX.Y.Z-<sha>-nix` |
|
||||
| Build type | Tag format | Example |
|
||||
|------------|-----------|---------|
|
||||
| Dockerfile | `vX.Y.Z-<sha>` | `v2.2.17-abc1234` |
|
||||
| Nix | `vX.Y.Z-<sha>-nix` | `v2.17.0-abc1234-nix` |
|
||||
|
||||
Where:
|
||||
- `X.Y.Z` is the version of the most relevant bundled app (e.g., miniflux `2.2.5`, navidrome `0.53.3`)
|
||||
- `<sha>` is the short commit SHA of the source tree used for the build
|
||||
|
||||
The `-main` tag indicates a build from the merged main branch, suitable for production deployment. Non-main builds (manual dispatch) omit this suffix.
|
||||
- `X.Y.Z` is the version of the most relevant bundled app (e.g., miniflux `2.2.17`, navidrome `0.60.3`)
|
||||
- `<sha>` is the 7-char short commit SHA of the source tree used for the build
|
||||
|
||||
### What This Replaces
|
||||
|
||||
- The `container-tag-and-release` mise task is **renamed and repurposed** to `container-build-and-release` — it triggers a manual workflow dispatch instead of creating git tags. It sends the current `HEAD` SHA so that it works from any branch, not just main
|
||||
- The `container-tag-and-release` mise task is **replaced** by `container-build-and-release` — it triggers a manual workflow dispatch instead of creating git tags
|
||||
- Git tags of the form `<container>-vX.Y.Z` are no longer used to trigger builds
|
||||
- The `container-list` mise task should be updated to display the new tag format
|
||||
- The `container-list` mise task displays the new tag format
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `.forgejo/workflows/build-container.yaml` | Replace tag trigger with path + dispatch triggers; compute version and SHA; push multiple tags |
|
||||
| `.forgejo/workflows/build-container.yaml` | Replace tag trigger with path + dispatch triggers; compute version and SHA |
|
||||
| `.forgejo/workflows/build-container-nix.yaml` | Same trigger changes; add `-nix` suffix to new tag format |
|
||||
| `.dagger/src/blumeops_ci/main.py` | Accept SHA parameter; publish with new tag format |
|
||||
| `mise-tasks/container-build-and-release` | Rename from `container-tag-and-release`; trigger workflow dispatch with current HEAD SHA |
|
||||
| `mise-tasks/container-list` | Update tag display for new format |
|
||||
| `docs/how-to/deployment/build-container-image.md` | Document new workflow |
|
||||
| `mise-tasks/container-build-and-release` | New task replacing `container-tag-and-release`; triggers workflow dispatch |
|
||||
| `mise-tasks/container-list` | Updated tag display for new format |
|
||||
| `docs/how-to/deployment/build-container-image.md` | Updated documentation |
|
||||
|
||||
## Interaction With Other Prereqs
|
||||
|
||||
- **[[enforce-tag-immutability]]** — Commit SHA tags are inherently unique, reducing the scope of immutability enforcement to the `-main` rolling tag (if that is treated as mutable/latest) or eliminating it entirely if `-main` tags are also SHA-qualified (as proposed above)
|
||||
- **[[enforce-tag-immutability]]** — Commit SHA tags are inherently unique, reducing the scope of immutability enforcement
|
||||
- **[[wire-ci-registry-auth]]** — Auth changes apply regardless of tagging scheme; no conflict
|
||||
|
||||
## Verification
|
||||
|
||||
- [ ] Push to main modifying `containers/nettest/` triggers both Docker and Nix builds
|
||||
- [ ] Resulting image tags match `vX.Y.Z-<sha>` and `vX.Y.Z-<sha>-main` format
|
||||
- [ ] Nix tags have `-nix` suffix
|
||||
- [ ] Manual workflow dispatch builds with correct tags (no `-main` suffix)
|
||||
- [ ] `mise run container-list` shows new tag format
|
||||
- [ ] Existing deployments referencing old tags still work (images not deleted)
|
||||
|
||||
## Related
|
||||
|
||||
- [[harden-zot-registry]] — Parent goal
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ BlumeOps operations are driven by mise tasks. Run `mise tasks` to list all avail
|
|||
| `pr-comments` | Check unresolved PR comments during review |
|
||||
| `blumeops-tasks` | Find pending tasks from Todoist |
|
||||
| `container-list` | View available container images and tags |
|
||||
| `container-tag-and-release` | Release a new container image version |
|
||||
| `container-build-and-release` | Trigger container build workflows |
|
||||
| `dns-preview` | Preview DNS changes before applying |
|
||||
| `dns-up` | Apply DNS changes via Pulumi |
|
||||
| `tailnet-preview` | Preview Tailscale ACL changes |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue