Skip to content

Add release automation; publishing a tag leaves action.yml pointing at the previous digest #5

Description

@robinbowes

Summary

CI is in good shape. Release automation is the gap: cutting a version is
entirely manual, and publish-image.yml produces an image that nothing
references until a human edits action.yml by hand.

Surfaced on 2026-08-24 while pinning this action from yo61/python-template.

What exists

workflowtriggerdoes
ci.ymlpushnpm lint, jest with coverage, docker build
commitlint.ymlpush, pull_requestdogfoods the action twice — once against the branch Dockerfile, once against the published digest
publish-image.ymlpush tag v*builds, pushes :VERSION and :MAJOR to ghcr, pulls it back to smoke-test, reports the digest to the step summary
dependabot.ymlweeklydependency updates

Actions are SHA-pinned with version comments, permissions: {} at workflow
level, persist-credentials: false throughout. No complaints.

What's missing

1. No version-bump / changelog / tag automation

Nothing computes the next version, writes CHANGELOG.md, or creates the tag.
publish-image.yml only reacts to a tag someone pushed by hand.

.versionrc.json is still present from upstream's commit-and-tag-version
setup, but the release job that used it was removed when this fork replaced
Docker Hub publishing with ghcr — see the comment in ci.yml. So the config is
orphaned: it configures a tool nothing runs.

Every other yo61 repo uses release-please. That would fit here, with the
release PR merge creating the tag that publish-image.yml already reacts to.

2. Publishing a tag produces an image nothing points at

action.yml pins the runtime image by digest (correctly — a SHA pin downstream
is worthless if the image tag it resolves to is mutable). But that makes
releasing circular:

  1. push tag vX.Y.Z
  2. publish-image.yml builds a new image with a new digest
  3. action.yml still names the previous digest
  4. a human reads the step summary and commits the new digest
  5. that commit is not in the tag

The workflow acknowledges this — its last step prints "Update action.yml to
this digest if it is not already."
— but nothing enforces or automates it.

Live example, caused by cutting v6.3.1 today:

tag v6.3.1 -> commit 501ff7bb
action.yml @ 501ff7bb -> sha256:78d47699… (the v6.3.0 image)
publish-image.yml built -> sha256:59972d25… (pushed as :6.3.1 and :6)

Nothing is broken: v6.3.1 resolves to an action.yml naming a real,
published, smoke-tested image, and consumers pinning that SHA get exactly what
was tested. But ghcr…:6.3.1 is now an orphan nothing references, and the
mutable :6 tag was repointed at it.

Worth deciding what the intended invariant is. Options:

  • Two-phase release — publish the image first, then a follow-up commit
    updates action.yml to the new digest and that commit gets tagged. Correct,
    but two commits per release.
  • Build reproducibly and pre-compute — derive the digest before tagging so
    action.yml can name it in the same commit. Needs a reproducible build.
  • Accept it and automate step 4 — let the publish workflow open a PR
    updating action.yml, and treat the digest as trailing the tag by one commit.
    Simplest, and matches how it already behaves.

3. Minor: checkout pin lags in one workflow

publish-image.yml uses actions/checkout@de0fac2e… (v6.0.2); ci.yml and
commitlint.yml use @3d3c42e5… (v7.0.1). Harmless, but the inconsistency
suggests Dependabot did not group them.

Out of scope

  • The CI workflows themselves — they work and follow house standards.
  • Migrating off ghcr.

Note on the frozen dependency resolution

Not a defect, but a consequence worth recording: the digest pin also freezes
npm resolution. package.json declares @commitlint/cli ^21.2.1, but the
caret resolved when the image was built, so the action runs whatever was
current then, not what npm serves today. Commitlint will not drift forward in
CI on its own — a bump requires a rebuild and a digest change, which is exactly
what item 2 is about.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions