feat(pdftract-4b0z): implement publish-if-tag step for GitHub Releases
Implement the publish-if-tag step in pdftract-ci that activates on version tags (v*.*.*) and publishes cross-compiled binaries to GitHub Releases. Changes: - Add tools/extract-release-notes.sh script for CHANGELOG parsing - Update publish-if-tag template in pdftract-ci.yaml: - Downloads all 5 build artifacts from build-matrix - Generates SHA256SUMS checksums - Extracts release notes from CHANGELOG.md - Creates GitHub Release via gh CLI - Supports both stable and pre-release tags (--prerelease flag) - Uses --clobber for idempotent re-runs The step uses Chainguard's gh:latest image and authenticates via github-pdftract-release Secret (GH_TOKEN key). Optional signing infrastructure is deferred to Release Engineering epic. Co-Authored-By: Claude Code (glm-4.7) <noreply@anthropic.com>
This commit is contained in:
parent
3c8ac46a3c
commit
a2b9e73a88
2 changed files with 223 additions and 13 deletions
|
|
@ -1079,32 +1079,156 @@ spec:
|
|||
|
||||
# === Publish If Tag ===
|
||||
# On milestone tags, upload binaries to GitHub Releases
|
||||
# Filled in by subsequent Phase 0 bead
|
||||
#
|
||||
# CRITICAL: All cargo commands MUST use --locked (or --locked --frozen)
|
||||
# The build step already uses --locked, so artifacts are reproducible.
|
||||
# This step only uploads pre-built binaries to GitHub Releases.
|
||||
# This step activates only when the workflow's ref parameter is a version tag
|
||||
# matching v*.*.* (e.g., v0.1.0, v1.2.3). Pre-release tags (v0.1.0-rc1) are
|
||||
# uploaded with the --prerelease flag.
|
||||
#
|
||||
# The step:
|
||||
# 1. Downloads all five build artifacts from build-matrix
|
||||
# 2. Generates SHA256SUMS checksums
|
||||
# 3. Extracts release notes from CHANGELOG.md
|
||||
# 4. Creates or updates the GitHub Release with all assets
|
||||
#
|
||||
# Uses Chainguard's minimal gh CLI image with jq for JSON processing.
|
||||
#
|
||||
# CRITICAL: Uses --clobber flag for idempotency - re-running the workflow
|
||||
# on the same tag overwrites assets rather than failing.
|
||||
- name: publish-if-tag
|
||||
inputs:
|
||||
artifacts:
|
||||
- name: pdftract-linux-x86_64-musl
|
||||
from: "{{tasks.build-matrix.tasks.build-linux-x86_64-musl.outputs.artifacts.pdftract-binary}}"
|
||||
path: /artifacts/pdftract-x86_64-unknown-linux-musl
|
||||
- name: pdftract-linux-aarch64-musl
|
||||
from: "{{tasks.build-matrix.tasks.build-linux-aarch64-musl.outputs.artifacts.pdftract-binary}}"
|
||||
path: /artifacts/pdftract-aarch64-unknown-linux-musl
|
||||
- name: pdftract-darwin-x86_64
|
||||
from: "{{tasks.build-matrix.tasks.build-darwin-x86_64.outputs.artifacts.pdftract-binary}}"
|
||||
path: /artifacts/pdftract-x86_64-apple-darwin
|
||||
- name: pdftract-darwin-aarch64
|
||||
from: "{{tasks.build-matrix.tasks.build-darwin-aarch64.outputs.artifacts.pdftract-binary}}"
|
||||
path: /artifacts/pdftract-aarch64-apple-darwin
|
||||
- name: pdftract-windows-x86_64-gnu
|
||||
from: "{{tasks.build-matrix.tasks.build-windows-x86_64-gnu.outputs.artifacts.pdftract-binary}}"
|
||||
path: /artifacts/pdftract-x86_64-pc-windows-gnu.exe
|
||||
activeDeadlineSeconds: 600
|
||||
container:
|
||||
image: alpine:3.19
|
||||
command: [sh, -c]
|
||||
image: cgr.dev/chainguard/gh:latest
|
||||
command: [bash, -c]
|
||||
args:
|
||||
- |
|
||||
# Placeholder: publish step
|
||||
echo "Publish step - to be implemented by Phase 0 sibling bead"
|
||||
exit 0
|
||||
set -eo pipefail
|
||||
|
||||
echo "=========================================="
|
||||
echo "Publishing GitHub Release"
|
||||
echo "=========================================="
|
||||
|
||||
REF="{{workflow.parameters.ref}}"
|
||||
TAG="${REF#refs/tags/}"
|
||||
REPO="{{workflow.parameters.repo-url%.git}}"
|
||||
ARTIFACTS_DIR="/artifacts"
|
||||
RELEASE_NOTES_FILE="/tmp/release-notes.md"
|
||||
SHA256SUMS_FILE="/tmp/SHA256SUMS"
|
||||
|
||||
echo "Tag: $TAG"
|
||||
echo "Repository: $REPO"
|
||||
|
||||
# Verify all artifacts are present
|
||||
echo "=== Verifying build artifacts ==="
|
||||
EXPECTED_ARTIFACTS=(
|
||||
"pdftract-x86_64-unknown-linux-musl"
|
||||
"pdftract-aarch64-unknown-linux-musl"
|
||||
"pdftract-x86_64-apple-darwin"
|
||||
"pdftract-aarch64-apple-darwin"
|
||||
"pdftract-x86_64-pc-windows-gnu.exe"
|
||||
)
|
||||
|
||||
MISSING=0
|
||||
for artifact in "${EXPECTED_ARTIFACTS[@]}"; do
|
||||
if [ ! -f "$ARTIFACTS_DIR/$artifact" ]; then
|
||||
echo "ERROR: Missing artifact: $artifact" >&2
|
||||
MISSING=$((MISSING + 1))
|
||||
else
|
||||
echo " Found: $artifact"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$MISSING" -gt 0 ]; then
|
||||
echo "ERROR: $MISSING artifacts missing, cannot publish release" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Generate SHA256SUMS
|
||||
echo "=== Generating SHA256SUMS ==="
|
||||
cd "$ARTIFACTS_DIR"
|
||||
for artifact in "${EXPECTED_ARTIFACTS[@]}"; do
|
||||
sha256sum "$artifact" >> "$SHA256SUMS_FILE"
|
||||
done
|
||||
cat "$SHA256SUMS_FILE"
|
||||
|
||||
# Extract release notes from CHANGELOG
|
||||
echo "=== Extracting release notes ==="
|
||||
if [ -f "/workspace/tools/extract-release-notes.sh" ]; then
|
||||
/workspace/tools/extract-release-notes.sh "$TAG" "/workspace/CHANGELOG.md" > "$RELEASE_NOTES_FILE"
|
||||
else
|
||||
# Fallback if script not found
|
||||
cat > "$RELEASE_NOTES_FILE" <<EOF
|
||||
## Release $TAG
|
||||
|
||||
See [PR history](https://github.com/jedarden/pdftract/commits/$TAG) for detailed changes.
|
||||
EOF
|
||||
fi
|
||||
echo "Release notes:"
|
||||
cat "$RELEASE_NOTES_FILE"
|
||||
|
||||
# Detect pre-release tags (e.g., v0.1.0-rc1, v0.1.0-alpha.1)
|
||||
PRERELEASE_FLAG=""
|
||||
if [[ "$TAG" =~ -[a-zA-Z] ]]; then
|
||||
PRERELEASE_FLAG="--prerelease"
|
||||
echo "Pre-release tag detected, will mark as pre-release"
|
||||
fi
|
||||
|
||||
# Configure git for gh auth
|
||||
git config --global credential.helper store
|
||||
echo "https://x-access-token:${GH_TOKEN}@github.com" > ~/.git-credentials
|
||||
|
||||
# Create or update release
|
||||
echo "=== Creating/updating GitHub release ==="
|
||||
if gh release view "$TAG" --repo "$REPO" &>/dev/null; then
|
||||
echo "Release $TAG already exists, updating assets"
|
||||
gh release upload "$TAG" "$SHA256SUMS_FILE" ${EXPECTED_ARTIFACTS[@]/#/$ARTIFACTS_DIR\/} --repo "$REPO" --clobber
|
||||
else
|
||||
echo "Creating new release $TAG"
|
||||
gh release create "$TAG" \
|
||||
--repo "$REPO" \
|
||||
--title "$TAG" \
|
||||
--notes-file "$RELEASE_NOTES_FILE" \
|
||||
$PRERELEASE_FLAG
|
||||
|
||||
# Upload assets to the newly created release
|
||||
echo "=== Uploading release assets ==="
|
||||
gh release upload "$TAG" "$SHA256SUMS_FILE" ${EXPECTED_ARTIFACTS[@]/#/$ARTIFACTS_DIR\/} --repo "$REPO"
|
||||
fi
|
||||
|
||||
# Verify release
|
||||
echo "=== Verifying release ==="
|
||||
gh release view "$TAG" --repo "$REPO"
|
||||
|
||||
echo "=========================================="
|
||||
echo "Release published successfully"
|
||||
echo "URL: https://github.com/jedarden/pdftract/releases/tag/$TAG"
|
||||
echo "=========================================="
|
||||
env:
|
||||
- name: GH_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: github-webhook-secret
|
||||
key: token
|
||||
name: github-pdftract-release
|
||||
key: GH_TOKEN
|
||||
optional: true
|
||||
volumeMounts:
|
||||
- name: workspace
|
||||
mountPath: /workspace
|
||||
- name: cargo-cache
|
||||
mountPath: /cache/cargo
|
||||
resources:
|
||||
requests:
|
||||
cpu: 500m
|
||||
|
|
|
|||
86
tools/extract-release-notes.sh
Executable file
86
tools/extract-release-notes.sh
Executable file
|
|
@ -0,0 +1,86 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# extract-release-notes.sh <tag> <changelog-path>
|
||||
#
|
||||
# Extracts release notes from CHANGELOG.md for a given version tag.
|
||||
# Outputs markdown content suitable for GitHub release notes.
|
||||
#
|
||||
# Usage:
|
||||
# extract-release-notes.sh v0.1.0 CHANGELOG.md > release-notes.md
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 - Success (notes extracted or stub generated)
|
||||
# 1 - Usage error or file not found
|
||||
|
||||
TAG="${1:-}"
|
||||
CHANGELOG_PATH="${2:-CHANGELOG.md}"
|
||||
|
||||
if [ -z "$TAG" ]; then
|
||||
echo "Usage: $0 <tag> [changelog-path]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$CHANGELOG_PATH" ]; then
|
||||
echo "ERROR: Changelog file not found: $CHANGELOG_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Strip 'v' prefix if present for version matching
|
||||
VERSION="${TAG#v}"
|
||||
|
||||
# Extract the release notes for this version
|
||||
# Matches: ## [0.1.0] or ## [Unreleased]
|
||||
# Captures everything until the next ## header
|
||||
NOTES=$(awk -v version="$VERSION" '
|
||||
BEGIN { in_section = 0; found = 0 }
|
||||
|
||||
# Match version header: ## [0.1.0] or ## [0.1.0 - 2024-01-01]
|
||||
/^## \['"$version"'/ {
|
||||
in_section = 1
|
||||
found = 1
|
||||
next
|
||||
}
|
||||
|
||||
# Stop at next version header
|
||||
/^## \[/ && in_section {
|
||||
exit
|
||||
}
|
||||
|
||||
# Print lines within the section (skip empty lines at start)
|
||||
in_section && (!skip_empty || $0 != "") {
|
||||
if ($0 == "" && !found_content) {
|
||||
next
|
||||
}
|
||||
found_content = 1
|
||||
print
|
||||
skip_empty = 1
|
||||
}
|
||||
|
||||
END { exit found ? 0 : 1 }
|
||||
' "$CHANGELOG_PATH")
|
||||
|
||||
if [ $? -eq 0 ] && [ -n "$NOTES" ]; then
|
||||
# Successfully extracted notes
|
||||
echo "$NOTES"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# No notes found - generate stub
|
||||
cat <<EOF
|
||||
## Release $TAG
|
||||
|
||||
See [PR history](https://github.com/jedarden/pdftract/commits/$TAG) for detailed changes.
|
||||
|
||||
EOF
|
||||
|
||||
# Try to find the previous tag for comparison URL
|
||||
PREV_TAG=$(git tag -l "v*" | sort -V | grep -B1 "^${TAG}$" | head -1 || true)
|
||||
|
||||
if [ -n "$PREV_TAG" ] && [ "$PREV_TAG" != "$TAG" ]; then
|
||||
cat <<EOF
|
||||
[Full changelog](https://github.com/jedarden/pdftract/compare/$PREV_TAG...$TAG)
|
||||
EOF
|
||||
fi
|
||||
|
||||
exit 0
|
||||
Loading…
Add table
Reference in a new issue