Files
chemenu/.gitea/workflows/release.yml
T
torben d29d400dd3
CI / verify (push) Successful in 47s
Release / release (push) Successful in 35s
feat: Versionskandidat statt Bump-pro-Release - VERSION traegt -beta.N, version release fixiert (4.4.0, #42)
Files changed:
- .gitea/workflows/release.yml
- AGENTS.md
- CHANGES.md
- DEVELOPMENT.md
- README.md
- VERSION
- docs/version-model.md
- instructions/dev/version-parts.md
- tools/CONTRACT.md
- tools/chemenu/commands/dist_cmd.py
- tools/chemenu/commands/docs_verify.py
- tools/chemenu/commands/doctor.py
- tools/chemenu/commands/migrate_cmd.py
- tools/chemenu/commands/version_cmd.py
- tools/chemenu/kb_state.py
- tools/chemenu/tests/test_dist_cmd.py
- tools/chemenu/tests/test_docs_verify.py
- tools/chemenu/tests/test_doctor.py
- tools/chemenu/tests/test_migrate_cmd.py
- tools/chemenu/tests/test_version_cmd.py
- tools/chemenu/version.py
2026-09-03 22:19:41 +02:00

166 lines
6.6 KiB
YAML

# Publish a release when VERSION moves on main.
#
# The release artifact is exactly a `dist export` tree, packed with a top-level
# directory: unpack it, run instructions/setup-instance.md, and there is a
# working wiki instance - no checkout of this repo required. CI already proved
# that path works before this workflow ever runs.
#
# The tag is created here, by CI, and never by an agent: AGENTS.md invariant 5
# ("never call raw git commit/push") stays intact because nothing in a session
# has to tag anything.
#
# Auth is `${{ gitea.token }}` - the short-lived per-job token this Gitea
# instance issues - not a 1Password secret. Nothing here reaches outside the
# instance, so the Zero-Trust secret path that the container-build workflows
# use has nothing to carry.
name: Release
on:
push:
branches: [main]
paths:
- VERSION
jobs:
release:
runs-on: linux-docker
container:
image: debian:trixie-slim
permissions:
contents: write
env:
WIKITOOL_SESSION_ID: release-${{ github.run_id }}
WIKI_TRACE_DIR: /tmp/wikitool-trace
BUILD_DIR: /tmp/build
# The address a *reader* uses. `github.server_url` is whatever the runner
# registered against (an internal one here), which is right for the API
# call below and wrong for a URL baked into every distributed instance.
PUBLIC_BASE_URL: https://gitea.nehmer.net
steps:
- name: System dependencies
# `nodejs` is for act_runner, not for us - see the note in ci.yml.
run: |
set -eu
apt-get update -qq
apt-get install -y --no-install-recommends \
python3 python3-venv git nodejs ripgrep ca-certificates curl jq tar gzip
rm -rf /var/lib/apt/lists/*
- uses: actions/checkout@v7
- name: Tool environment
run: |
set -eu
git config --global --add safe.directory "$GITHUB_WORKSPACE"
python3 -m venv tools/.venv
tools/.venv/bin/pip install --quiet --upgrade pip
tools/.venv/bin/pip install --quiet -r tools/requirements.txt
- name: Resolve the version and refuse to re-release it
id: version
env:
API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
TOKEN: ${{ gitea.token }}
run: |
set -eu
version="$(cat VERSION | tr -d '[:space:]')"
# A running candidate (`X.Y.Z-beta.N`) is never released - betas are
# a dev-checkout state, not a distributed one (see
# instructions/dev/version-parts.md). This guard sits *before* the
# API query below: without it, every `version bump` on a candidate
# would push VERSION and trigger a wasted round-trip against the
# releases API for a tag that was never going to be created. Ending
# the job cleanly here (not `exit 1`) is what keeps a beta bump a
# normal, unremarkable push rather than a failing CI run - skipping
# every later step is what "cleanly" means in Actions: mark this one
# skip and gate the rest on it.
case "$version" in
*-beta.*)
echo "VERSION is a running candidate (${version}) - nothing to release. Skipping."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
;;
esac
echo "skip=false" >> "$GITHUB_OUTPUT"
tag="v${version}"
echo "version=${version}" >> "$GITHUB_OUTPUT"
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
status="$(curl -s -o /dev/null -w '%{http_code}' \
-H "Authorization: token ${TOKEN}" "${API}/releases/tags/${tag}")"
if [ "$status" = "200" ]; then
echo "Release ${tag} already exists. VERSION was touched without being raised;"
echo 'bump it with `tools/wikitool version bump` instead of re-releasing.'
exit 1
fi
echo "No release ${tag} yet - proceeding."
- name: Release notes from CHANGES.md
# `version notes` fails when the changelog has no entry for this
# version, which is the last place that mistake can still be caught.
if: steps.version.outputs.skip != 'true'
run: |
set -eu
tools/wikitool docs verify
tools/wikitool version notes > /tmp/release-notes.md
cat /tmp/release-notes.md
- name: Build the distribution tarball
id: build
if: steps.version.outputs.skip != 'true'
env:
VERSION: ${{ steps.version.outputs.version }}
TAG: ${{ steps.version.outputs.tag }}
run: |
set -eu
name="chemenu-stack-${VERSION}"
mkdir -p "$BUILD_DIR"
tools/wikitool dist export "${BUILD_DIR}/${name}" \
--source-repo "${PUBLIC_BASE_URL}/${GITHUB_REPOSITORY}" \
--source-commit "${GITHUB_SHA}" \
--release-url "${PUBLIC_BASE_URL}/${GITHUB_REPOSITORY}/releases/tag/${TAG}" \
--update-url "${PUBLIC_BASE_URL}/api/v1/repos/${GITHUB_REPOSITORY}/releases/latest"
tar -czf "${BUILD_DIR}/${name}.tar.gz" -C "$BUILD_DIR" "$name"
( cd "$BUILD_DIR" && sha256sum "${name}.tar.gz" > "${name}.tar.gz.sha256" )
cat "${BUILD_DIR}/${name}.tar.gz.sha256"
echo "name=${name}" >> "$GITHUB_OUTPUT"
- name: Publish the release
if: steps.version.outputs.skip != 'true'
env:
API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
TOKEN: ${{ gitea.token }}
TAG: ${{ steps.version.outputs.tag }}
NAME: ${{ steps.build.outputs.name }}
run: |
set -eu
# Creating the release creates the tag, pinned to this commit.
payload="$(jq -n \
--arg tag "$TAG" \
--arg target "$GITHUB_SHA" \
--arg name "$TAG" \
--rawfile body /tmp/release-notes.md \
'{tag_name: $tag, target_commitish: $target, name: $name, body: $body,
draft: false, prerelease: false}')"
release="$(curl -sS -f -X POST "${API}/releases" \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
-d "$payload")"
id="$(printf '%s' "$release" | jq -r '.id')"
echo "Created release ${TAG} (id ${id})."
for asset in "${NAME}.tar.gz" "${NAME}.tar.gz.sha256"; do
curl -sS -f -X POST "${API}/releases/${id}/assets?name=${asset}" \
-H "Authorization: token ${TOKEN}" \
-F "attachment=@${BUILD_DIR}/${asset}" > /dev/null
echo "Uploaded ${asset}."
done
echo "Done: ${PUBLIC_BASE_URL}/${GITHUB_REPOSITORY}/releases/tag/${TAG}"