Fully automated version management and package publishing (in a container image)
  • Python 68.5%
  • Shell 26.4%
  • Dockerfile 5.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-11-04 07:40:32 +00:00
.agents/skills bd init: initialize beads issue tracking 2026-07-31 08:39:43 +02:00
.beads feat: ✨ drive releases from the Tekton Task via hack/release.sh 2026-08-19 14:09:27 +02:00
.claude chore: 🔧 add OpenSpec and caveman agent skills 2026-07-31 11:23:37 +02:00
.codex bd init: initialize beads issue tracking 2026-07-31 08:39:43 +02:00
.tekton feat: ✨ drive releases from the Tekton Task via hack/release.sh 2026-08-19 14:09:27 +02:00
docs feat: ✨ drive releases from the Tekton Task via hack/release.sh 2026-08-19 14:09:27 +02:00
hack feat: ✨ drive releases from the Tekton Task via hack/release.sh 2026-08-19 14:09:27 +02:00
openspec/changes/add-release-runner-task feat: ✨ drive releases from the Tekton Task via hack/release.sh 2026-08-19 14:09:27 +02:00
skills docs: 📝 package the release procedures as agent skills 2026-08-19 14:10:33 +02:00
.containerignore build: reorganize the pipelines: test on main, release on tag. now for real! 2025-02-25 19:59:37 +01:00
.gitignore bd init: initialize beads issue tracking 2026-07-31 08:39:43 +02:00
.releaserc fix: 💚 make v* tag pushes trigger the release pipeline 2026-08-01 21:52:19 +02:00
AGENTS.md bd init: initialize beads issue tracking 2026-07-31 08:39:43 +02:00
CHANGELOG.md chore(release): 1.8.0 2026-08-01 21:34:14 +00:00
CLAUDE.md bd init: initialize beads issue tracking 2026-07-31 08:39:43 +02:00
Containerfile chore(deps): update quay.io/fedora/nodejs-22 docker digest to b257c1b 2026-11-04 07:38:39 +00:00
LICENSE feat: add Containerfile so that we can build a container image 2025-02-18 23:55:59 +01:00
OWNERS OWNERS hinzugefügt 2025-02-22 21:56:57 +00:00
README.md docs: 📝 package the release procedures as agent skills 2026-08-19 14:10:33 +02:00
renovate.json5 fix: 🔧 make the Renovate config load at all 2026-08-03 07:06:53 +02:00
skills-lock.json chore: 🔧 add OpenSpec and caveman agent skills 2026-07-31 11:23:37 +02:00

semantic-release

This is a containerized version of the semantic-release tool. Hopefully it will be maintained and released by the cyborgs!

podman run --rm -ti --userns=keep-id:uid=1001,gid=0 \
  -e NPM_TOKEN=npm_xxx -eEDITOR="echo 'Created-by: semantic-release' > $1" \
  -v $(pwd):/workspace:Z -v <path-to-gpg-home-dir>:/opt/app-root/src/.gnupg:Z -v <path-to-home-dot-ssh>:/opt/app-root/src/.ssh:Z \
  git.b4mad.industries/agentic-forges/semantic-release:v1.8.0 --debug --no-ci

https://github.com/semantic-release/semantic-release/issues/3065 was helpful wrt gpg-signed tags.

To onboard another repository on the forge, see docs/enabling-a-repository.md.

Agent skills

Both procedures are packaged as agent skills under skills/:

Skill Covers
b4mad-enable-release onboarding a repo — preconditions, .releaserc, dry run
b4mad-cut-release cutting or dry-running a release, and diagnosing one

Install them with npx skills. This forge serves source archives from the API path, not the web /archive/ path, so point the installer at the API:

npx skills add https://git.b4mad.industries/api/v1/repos/agentic-forges/semantic-release/archive/main.tar.gz \
  --skill b4mad-enable-release,b4mad-cut-release

Name the skills explicitly — --skill '*' would also pull in the vendored beads skill under .agents/skills/, which is not ours to redistribute.

Releasing a repository from the cluster

The runner is the Tekton Task in .tekton/tasks/semantic-release.yaml. It clones the target over SSH and pushes a GPG-signed tag as b4mad-release-agent; the target supplies its own .releaserc and needs no CI of its own.

Two ways to drive it:

hack/release.sh --repo/--ref/--dry-run, applies the Task, waits, prints the log and the released version. Start here
.tekton/release-repo.yaml The same TaskRun as a manifest, for when you want to edit params by hand or read them in review

hack/release-job.yaml is the pre-Tekton Job. Nothing drives it any more; it is kept as the fallback for a cluster without Pipelines. Retiring it is af-1da.

Prerequisites

One Secret in the namespace the run happens in — b4mad-forgejo today:

forgejo-agent-b4mad-release-agent
  ssh-privatekey    pushes the release commit and tag
  gpg-privatekey    signs them
  token             Forgejo API; only used if the target's .releaserc
                    lists @ribbon-studios/semantic-release-forgejo

It is created by forge-agents/create-forge-agent.py, not by anything here. The ServiceAccount is pipeline. The forge's SSH host key fingerprint is pinned in the Task as SHA256:LFo9B+TvN+sXwkUBW58RxU4QVyqk8rRaohW5UPFiuYw on port 2222; the Task keyscans the forge and fails closed if the live key does not match, so a genuine host key rotation is a deliberate edit, never a blind one.

Triggering

./hack/release.sh --repo <owner>/<repo> --ref main --dry-run   # always first
./hack/release.sh --repo <owner>/<repo> --ref main

The script applies the Task before every run, so the cluster's copy cannot drift behind the one in git. --dry-run computes the next version and renders the notes without pushing; do that once before a repository's first real release, always.

By hand, without the script:

oc -n b4mad-forgejo apply -f .tekton/tasks/semantic-release.yaml   # once

# edit repo_slug in the template, then:
oc -n b4mad-forgejo create -f .tekton/release-repo.yaml
oc -n b4mad-forgejo get taskrun <name> -o jsonpath='{range .status.results[*]}{.name}={.value}{"\n"}{end}'

Results are released-version and released-ref. Both come back empty when there was nothing to release — that is success, not failure. On a dry run released-version is the computed version and released-ref is empty, because no tag was created.

If a run sits Pending forever, check oc get events for FailedMount: a missing or misnamed agent Secret does not fail the TaskRun, it just never starts. The template's timeout: 30m bounds it.

Configuration

GnuPG

[https://github.com/isislovecruft/python-gnupg/issues/137#issuecomment-459043779] was a helpful hint, as parts of the magic is configured in the ~/.gnupg/gpg.conf file.

$ cat ~/.gnupg/gpg.conf
no-greeting
pinentry-mode loopback
[...]

Environment Variables

GIT_AUTHOR_NAME="b4mad-release-bot"
GIT_AUTHOR_EMAIL="[email protected]"
GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"

Git

Within the container we have to configure /workspace/ to be a safe directory for git operations, so the Containerfile contains the following lines:

RUN git config --global --add safe.directory /workspace