Replace hardcoded TODO markers with Forgejo template variables (${REPO_NAME},
${REPO_OWNER}, etc.) so new repos created from this template are auto-customized.
Use Forgejo Actions context variables in build.yaml for dynamic FORGE_URL.
Hardcode forge.eblu.me as the Forgejo instance. Update CLAUDE.md and README.md
to reflect reduced manual setup steps.
Python class names kept as manual TODO (same as directory rename) since template
variables in Python code positions aren't valid syntax for linters.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
240 lines
7.9 KiB
YAML
240 lines
7.9 KiB
YAML
# Release Workflow
|
|
#
|
|
# Creates a versioned release with build artifacts.
|
|
# Currently includes:
|
|
# - Documentation site (Quartz static build)
|
|
# - Changelog (built from towncrier fragments)
|
|
#
|
|
# Usage:
|
|
# 1. Go to Actions > Build > Run workflow
|
|
# 2. Select version bump type (patch/minor/major) or choose specific version
|
|
# 3. The workflow creates a release with attached artifacts
|
|
|
|
name: Build
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
version_type:
|
|
description: 'Version bump type'
|
|
required: true
|
|
default: 'BUMP_PATCH'
|
|
type: choice
|
|
options:
|
|
- BUMP_PATCH
|
|
- BUMP_MINOR
|
|
- BUMP_MAJOR
|
|
- SPECIFIC_VERSION
|
|
specific_version:
|
|
description: 'Specific version (only used when version_type is SPECIFIC_VERSION, e.g., v1.2.0)'
|
|
required: false
|
|
default: ''
|
|
type: string
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: k8s
|
|
steps:
|
|
- name: Resolve version
|
|
id: version
|
|
run: |
|
|
VERSION_TYPE="${{ inputs.version_type }}"
|
|
SPECIFIC_VERSION="${{ inputs.specific_version }}"
|
|
|
|
# Fetch latest release
|
|
FORGE_URL="${{ github.server_url }}/api/v1/repos/${{ github.repository }}"
|
|
echo "Fetching latest release..."
|
|
LATEST=$(curl -s "${FORGE_URL}/releases/latest" | jq -r '.tag_name // empty' || true)
|
|
|
|
if [ -z "$LATEST" ]; then
|
|
LATEST="v0.0.0"
|
|
echo "No previous releases found, using base version: $LATEST"
|
|
else
|
|
echo "Latest release: $LATEST"
|
|
fi
|
|
|
|
# Parse current version components (strip 'v' prefix)
|
|
CURRENT="${LATEST#v}"
|
|
MAJOR=$(echo "$CURRENT" | cut -d. -f1)
|
|
MINOR=$(echo "$CURRENT" | cut -d. -f2)
|
|
PATCH=$(echo "$CURRENT" | cut -d. -f3)
|
|
|
|
case "$VERSION_TYPE" in
|
|
BUMP_MAJOR)
|
|
VERSION="v$((MAJOR + 1)).0.0"
|
|
echo "Bumping major: $LATEST -> $VERSION"
|
|
;;
|
|
BUMP_MINOR)
|
|
VERSION="v${MAJOR}.$((MINOR + 1)).0"
|
|
echo "Bumping minor: $LATEST -> $VERSION"
|
|
;;
|
|
BUMP_PATCH)
|
|
VERSION="v${MAJOR}.${MINOR}.$((PATCH + 1))"
|
|
echo "Bumping patch: $LATEST -> $VERSION"
|
|
;;
|
|
SPECIFIC_VERSION)
|
|
if [ -z "$SPECIFIC_VERSION" ]; then
|
|
echo "Error: specific_version is required when version_type is SPECIFIC_VERSION"
|
|
exit 1
|
|
fi
|
|
if [[ ! "$SPECIFIC_VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
echo "Error: Version must be in format vX.Y.Z (e.g., v1.0.0)"
|
|
exit 1
|
|
fi
|
|
VERSION="$SPECIFIC_VERSION"
|
|
echo "Using specific version: $VERSION"
|
|
;;
|
|
*)
|
|
echo "Error: Unknown version_type: $VERSION_TYPE"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# Check if this version already exists
|
|
if curl -sf "${FORGE_URL}/releases/tags/$VERSION" > /dev/null 2>&1; then
|
|
echo "Error: Release $VERSION already exists"
|
|
exit 1
|
|
fi
|
|
|
|
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
echo "Building release: $VERSION"
|
|
|
|
- name: Checkout
|
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Build changelog
|
|
id: changelog
|
|
run: |
|
|
VERSION="${{ steps.version.outputs.version }}"
|
|
|
|
FRAGMENTS=$(find docs/changelog.d -name "*.md" -not -name ".gitkeep" 2>/dev/null | wc -l)
|
|
|
|
if [ "$FRAGMENTS" -gt 0 ]; then
|
|
echo "Found $FRAGMENTS changelog fragments, building changelog..."
|
|
uvx towncrier build --version "$VERSION" --yes
|
|
echo "changelog_updated=true" >> "$GITHUB_OUTPUT"
|
|
|
|
# Extract the changelog section for this release
|
|
RELEASE_NOTES=$(awk -v ver="$VERSION" '
|
|
/^## \[/ {
|
|
if (found) exit
|
|
if (index($0, "[" ver "]")) found=1
|
|
}
|
|
found {print}
|
|
' CHANGELOG.md | tail -n +2)
|
|
|
|
echo "$RELEASE_NOTES" > /tmp/release_notes.md
|
|
echo "Release notes extracted for $VERSION"
|
|
else
|
|
echo "No changelog fragments found, skipping towncrier"
|
|
echo "changelog_updated=false" >> "$GITHUB_OUTPUT"
|
|
echo "" > /tmp/release_notes.md
|
|
fi
|
|
|
|
- name: Build docs
|
|
run: |
|
|
VERSION="${{ steps.version.outputs.version }}"
|
|
TARBALL="docs-${VERSION}.tar.gz"
|
|
echo "Building docs via Dagger..."
|
|
dagger call build-docs --src=. --version="$VERSION" \
|
|
export --path="./$TARBALL"
|
|
echo "Build complete!"
|
|
ls -lh "$TARBALL"
|
|
|
|
- name: Create release
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
VERSION="${{ steps.version.outputs.version }}"
|
|
TARBALL="docs-${VERSION}.tar.gz"
|
|
CHANGELOG_UPDATED="${{ steps.changelog.outputs.changelog_updated }}"
|
|
|
|
FORGE_URL="${{ github.server_url }}/api/v1/repos/${{ github.repository }}"
|
|
|
|
echo "Creating release $VERSION..."
|
|
|
|
{
|
|
echo "Release $VERSION"
|
|
echo ""
|
|
|
|
if [ "$CHANGELOG_UPDATED" = "true" ] && [ -s /tmp/release_notes.md ]; then
|
|
echo "## What's Changed"
|
|
echo ""
|
|
cat /tmp/release_notes.md
|
|
echo ""
|
|
fi
|
|
|
|
echo "## Documentation"
|
|
echo ""
|
|
echo "Download \`$TARBALL\` for the documentation site build."
|
|
} > /tmp/release_body.txt
|
|
|
|
RELEASE_DATA=$(jq -n \
|
|
--arg tag "$VERSION" \
|
|
--arg name "Release $VERSION" \
|
|
--rawfile body /tmp/release_body.txt \
|
|
'{tag_name: $tag, name: $name, body: $body, draft: false, prerelease: false}')
|
|
|
|
RELEASE_RESPONSE=$(curl -s \
|
|
-X POST \
|
|
-H "Content-Type: application/json" \
|
|
-H "Authorization: token $GITHUB_TOKEN" \
|
|
-d "$RELEASE_DATA" \
|
|
"${FORGE_URL}/releases")
|
|
|
|
echo "API Response: $RELEASE_RESPONSE"
|
|
|
|
RELEASE_ID=$(echo "$RELEASE_RESPONSE" | jq -r '.id')
|
|
|
|
if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "null" ]; then
|
|
echo "Error: Failed to create release"
|
|
exit 1
|
|
fi
|
|
|
|
echo "Created release ID: $RELEASE_ID"
|
|
|
|
# Upload the asset
|
|
echo "Uploading $TARBALL..."
|
|
curl -s \
|
|
-X POST \
|
|
-H "Content-Type: application/gzip" \
|
|
-H "Authorization: token $GITHUB_TOKEN" \
|
|
--data-binary "@$TARBALL" \
|
|
"${FORGE_URL}/releases/$RELEASE_ID/assets?name=$TARBALL"
|
|
|
|
echo ""
|
|
echo "Release created successfully!"
|
|
|
|
- name: Commit changelog changes
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
VERSION="${{ steps.version.outputs.version }}"
|
|
CHANGELOG_UPDATED="${{ steps.changelog.outputs.changelog_updated }}"
|
|
|
|
if [ "$CHANGELOG_UPDATED" != "true" ]; then
|
|
echo "No changelog changes to commit"
|
|
exit 0
|
|
fi
|
|
|
|
git config user.name "Forgejo Actions"
|
|
git config user.email "actions@forge.eblu.me"
|
|
|
|
git add CHANGELOG.md docs/changelog.d/
|
|
|
|
if git diff --cached --quiet; then
|
|
echo "No changes to commit"
|
|
else
|
|
git commit -m "Update changelog for $VERSION [skip ci]"
|
|
git push origin HEAD:main
|
|
echo "Changelog changes committed and pushed"
|
|
fi
|
|
|
|
- name: Summary
|
|
run: |
|
|
VERSION="${{ steps.version.outputs.version }}"
|
|
echo "================================================"
|
|
echo "Release: $VERSION"
|
|
echo "================================================"
|