conductor

CI task system
git clone git://git.finwo.net/app/conductor
Log | Files | Refs | README | LICENSE

commit f25e624d5215eee6865a9db7febf341aa884e41a
parent c8d6358c3d6356be42d2cd801a5da41d2c9b854a
Author: finwo <finwo@pm.me>
Date:   Mon, 21 Sep 2026 16:36:34 +0200

Publish branch tags, and keep :latest on main only

Diffstat:
M.conductor.yml | 7++++---
Mdeploy/publish.sh | 34+++++++++++++++++++++++++++-------
Mdocs/deployment.md | 19+++++++++++++------
3 files changed, 44 insertions(+), 16 deletions(-)

diff --git a/.conductor.yml b/.conductor.yml @@ -62,9 +62,9 @@ tasks: - apk add --no-cache git openssl curl - ./deploy/smoke.sh - # Pushes to Docker Hub. Restricted to main and release tags, so a branch - # build never moves :latest. Without the rule this job would run on every - # push, which is the whole reason only: exists. + # Pushes to Docker Hub. Restricted to main and release tags for now; + # publish.sh handles any branch, so widening this rule is all it takes to + # publish a branch under its own tag. curl is for the readme sync. publish: needs: [smoke, integration] requires: [docker] @@ -72,4 +72,5 @@ tasks: only: refs: [refs/heads/main, 'refs/tags/v*'] script: + - apk add --no-cache curl - ./deploy/publish.sh diff --git a/deploy/publish.sh b/deploy/publish.sh @@ -6,11 +6,12 @@ # # What gets pushed, from CONDUCTOR_REF: # -# refs/heads/main finwo/conductor:latest and :<short sha> -# refs/tags/v1.2.0 finwo/conductor:1.2.0, :1.2 and :latest +# refs/heads/main finwo/conductor:main and :latest +# refs/heads/topic finwo/conductor:topic +# refs/tags/v1.2.0 finwo/conductor:1.2.0 and :1.2 # -# A tag publishes :latest as well, so :latest follows releases once there -# are any, and follows main until then. +# A branch publishes its own name. main carries :latest on top of that, +# and nothing else ever moves it, so :latest is always what main is. # # Needs REGISTRY_USERNAME and REGISTRY_TOKEN as project variables. They are # masked in the log by the conductor, but this still keeps them off the @@ -37,6 +38,24 @@ SHORT=$(printf '%s' "${CONDUCTOR_SHA}" | cut -c1-12) log() { printf '\n== %s\n' "$*"; } fail() { printf '\nFAILED: %s\n' "$*" >&2; exit 1; } +# A docker tag is [A-Za-z0-9_][A-Za-z0-9._-]*, so a branch such as +# feature/x cannot be used as it stands. +docker_tag() { + tag=$(printf '%s' "$1" | tr -c 'A-Za-z0-9_.-' '-' | cut -c1-128) + case "${tag}" in + [!A-Za-z0-9_]*) tag="x${tag}" ;; + esac + printf '%s\n' "${tag}" +} + +# What a branch publishes on top of its own name. Give another branch an +# alias, a pre-release series for instance, by adding a line here. +branch_aliases() { + case "$1" in + main) printf 'latest\n' ;; + esac +} + # Turns a ref into the list of tags it should publish. # # A version tag also publishes the major.minor series, so v1.2.1 moves @@ -45,8 +64,10 @@ fail() { printf '\nFAILED: %s\n' "$*" >&2; exit 1; } tags_for_ref() { ref=$1 case "${ref}" in - refs/heads/main) - printf 'latest\n%s\n' "${SHORT}" + refs/heads/*) + branch=${ref#refs/heads/} + docker_tag "${branch}" + branch_aliases "${branch}" ;; refs/tags/v*) version=${ref#refs/tags/v} @@ -59,7 +80,6 @@ tags_for_ref() { # v1.2.3 -> 1.2, but v1.2 stays as it is. series=$(printf '%s' "${version}" | cut -d. -f1-2) [ "${series}" = "${version}" ] || printf '%s\n' "${series}" - printf 'latest\n' ;; *) fail "ref ${ref} does not publish anything; the only: rule in .conductor.yml should have prevented this" diff --git a/docs/deployment.md b/docs/deployment.md @@ -284,12 +284,19 @@ Publishing The images are built and pushed by the project's own pipeline, from `deploy/publish.sh`. It runs only on `main` and on `v*` tags, enforced by an `only:` rule in `.conductor.yml` rather than by a check inside the -script, so a branch build cannot move `latest` even by accident. - -| Ref | Tags pushed | -| ------------------ | ------------------------------------ | -| `refs/heads/main` | `latest`, `<short sha>` | -| `refs/tags/v1.2.3` | `1.2.3`, `1.2`, `latest` | +script. The script itself handles any branch, so publishing a branch is a +matter of widening that rule. + +| Ref | Tags pushed | +| ------------------- | ------------------ | +| `refs/heads/main` | `main`, `latest` | +| `refs/heads/topic` | `topic` | +| `refs/tags/v1.2.3` | `1.2.3`, `1.2` | + +A branch publishes its own name, with any aliases listed for it in +`branch_aliases`. Only `main` has one, `latest`, and nothing else moves +it, so `latest` is always whatever main is. Branch names are rewritten to +suit a docker tag, so `feature/x` publishes as `feature-x`. A tag that is not a plain version, `v1.2.3-rc1` for instance, is refused rather than guessed at.