softprops/action-gh-release talks to GitHub's REST API — it cannot publish anywhere else, so this workflow could never have actually published a release on this Gitea-hosted repo, reproducibility bug aside. Replaced the publish step with plain curl against Gitea's own /api/v1/repos/.../releases endpoints, using the repo-scoped token Gitea Actions already injects as secrets.GITHUB_TOKEN (same env var name as GitHub Actions, for exactly this kind of drop-in compatibility). Idempotent: deletes and recreates the release if one already exists for the tag, so a re-run after a transient failure doesn't just error on a duplicate tag. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
151 lines
6.6 KiB
YAML
151 lines
6.6 KiB
YAML
name: Release
|
|
|
|
# Publishes a Gitea Release for a version tag (vX.Y.Z). This project lives
|
|
# on a self-hosted Gitea instance (git.mp-mueller.de), not GitHub — despite
|
|
# the .github/workflows/ path (kept there because Gitea Actions picks up
|
|
# workflows from that path too, verified live: lint.yml/build-packages.yml
|
|
# both already run from here without any .gitea/workflows/ copy).
|
|
#
|
|
# By design, this workflow does NOT bump versions or modify podman.plg
|
|
# itself — that happens locally via `scripts/release.sh <version>`, which a
|
|
# maintainer reviews, commits, and tags *before* pushing the tag (see that
|
|
# script's own printed instructions). This workflow's only job is to:
|
|
# 1. Rebuild all packages from the tagged commit in a clean Slackware
|
|
# container (reproducibility check + provenance — we don't trust
|
|
# whatever a maintainer happened to have in their local dist/). This
|
|
# only actually verifies anything because
|
|
# scripts/lib/slackbuild-common.sh's sb_make_package() pins
|
|
# SOURCE_DATE_EPOCH to the tagged commit's timestamp before calling
|
|
# makepkg — without that, two separate builds of the identical commit
|
|
# produce byte-different .txz files (different file mtimes baked into
|
|
# the tar archive) and this whole verify step fails on the first
|
|
# package it happens to check, every time (found live: aardvark-dns).
|
|
# 2. Verify checksums match what's already committed in plugin/podman.plg
|
|
# at this tag (catches a release.sh run that wasn't followed by a
|
|
# matching commit — see the "Verify plg matches build" step).
|
|
# 3. Create the Gitea Release and attach the .txz packages, checksum
|
|
# manifests, and podman.plg, via Gitea's own REST API — NOT
|
|
# softprops/action-gh-release, which talks to GitHub's API and simply
|
|
# cannot publish anywhere else. Gitea Actions injects a real,
|
|
# repo-scoped API token as secrets.GITHUB_TOKEN (the same env var
|
|
# name GitHub Actions uses, for exactly this kind of drop-in
|
|
# compatibility) — permissions: contents: write above is what scopes
|
|
# that token to allow creating releases.
|
|
#
|
|
# See docs/ARCHITECTURE.md section 13 (Updates).
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- "v*.*.*"
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
env:
|
|
GITEA_HOST: git.mp-mueller.de
|
|
GITEA_REPO: magges/unraid-podman
|
|
|
|
jobs:
|
|
build:
|
|
name: Build release packages
|
|
uses: ./.github/workflows/build-packages.yml
|
|
|
|
publish:
|
|
name: Publish Gitea Release
|
|
needs: build
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Download built packages
|
|
uses: actions/download-artifact@v3
|
|
with:
|
|
name: ${{ needs.build.outputs.artifact-name }}
|
|
path: dist
|
|
|
|
- name: Re-verify checksums
|
|
run: scripts/checksums.sh dist
|
|
|
|
- name: Verify plugin/podman.plg matches these artifacts
|
|
# scripts/release.sh should already have been run locally, and its
|
|
# resulting podman.plg changes committed as part of this tag, before
|
|
# the tag was pushed. This step fails the release loudly if that
|
|
# didn't happen, instead of publishing a release whose plg points at
|
|
# MD5s that don't match the .txz files actually attached below.
|
|
run: |
|
|
set -eu
|
|
for f in dist/*.txz.md5; do
|
|
expected_md5=$(awk '{print $1}' "$f")
|
|
txz_name=$(basename "${f%.md5}")
|
|
if ! grep -qF "$expected_md5" plugin/podman.plg; then
|
|
echo "!! $txz_name's checksum ($expected_md5) is not referenced in plugin/podman.plg." >&2
|
|
echo "!! Did you forget to run scripts/release.sh and commit its changes before tagging?" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
echo "All package checksums are referenced in plugin/podman.plg — OK."
|
|
|
|
- name: Extract version from tag
|
|
id: version
|
|
run: echo "value=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Extract changelog section for this version
|
|
id: changelog
|
|
run: |
|
|
awk -v ver="${{ steps.version.outputs.value }}" '
|
|
$0 ~ "^## \\[" ver "\\]" { found=1; print; next }
|
|
found && /^## \[/ { exit }
|
|
found { print }
|
|
' CHANGELOG.md > /tmp/release-notes.md
|
|
echo "path=/tmp/release-notes.md" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Publish Gitea Release
|
|
env:
|
|
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
TAG: ${{ github.ref_name }}
|
|
run: |
|
|
set -eu
|
|
API="https://${GITEA_HOST}/api/v1/repos/${GITEA_REPO}"
|
|
case "${{ steps.version.outputs.value }}" in
|
|
0.*) prerelease=true ;;
|
|
*) prerelease=false ;;
|
|
esac
|
|
|
|
# Idempotent: if a release for this tag already exists (e.g. a
|
|
# re-run after a transient failure), delete it first rather than
|
|
# erroring on Gitea's own duplicate-tag conflict.
|
|
existing_id=$(curl -s -H "Authorization: token ${GITEA_TOKEN}" "${API}/releases/tags/${TAG}" | jq -r '.id // empty')
|
|
if [ -n "$existing_id" ]; then
|
|
echo "==> Deleting existing release id=$existing_id for tag $TAG"
|
|
curl -s -X DELETE -H "Authorization: token ${GITEA_TOKEN}" "${API}/releases/${existing_id}"
|
|
fi
|
|
|
|
body_json=$(jq -Rs '.' < "${{ steps.changelog.outputs.path }}")
|
|
payload=$(jq -n --arg tag "$TAG" --arg name "unraid-podman ${TAG}" --argjson body "$body_json" --argjson prerelease "$prerelease" \
|
|
'{tag_name: $tag, name: $name, body: $body, draft: false, prerelease: $prerelease}')
|
|
|
|
release_id=$(curl -s -X POST -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" \
|
|
-d "$payload" "${API}/releases" | jq -r '.id')
|
|
if [ -z "$release_id" ] || [ "$release_id" = "null" ]; then
|
|
echo "!! Could not create release (no id in response)" >&2
|
|
exit 1
|
|
fi
|
|
echo "==> Created release id=$release_id, uploading assets..."
|
|
|
|
for f in dist/*.txz dist/*.sha256 dist/*.md5 dist/CHECKSUMS.sha256 dist/CHECKSUMS.md5 plugin/podman.plg; do
|
|
name=$(basename "$f")
|
|
code=$(curl -s -o /tmp/upload_resp.json -w '%{http_code}' -X POST \
|
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
-F "attachment=@${f};filename=${name}" \
|
|
"${API}/releases/${release_id}/assets?name=${name}")
|
|
if [ "$code" != "201" ]; then
|
|
echo "!! Upload failed ($code) for $name:" >&2
|
|
cat /tmp/upload_resp.json >&2
|
|
exit 1
|
|
fi
|
|
echo " uploaded: $name"
|
|
done
|
|
|
|
echo "==> Published: https://${GITEA_HOST}/${GITEA_REPO}/releases/tag/${TAG}"
|