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:
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.