# BlumeOps Release Workflow # # Creates a versioned release of BlumeOps with all build artifacts. # Currently includes: # - Documentation site (Quartz static build) # - Changelog (built from towncrier fragments) # # Usage: # 1. Go to Actions > Build BlumeOps > Run workflow # 2. Select version bump type (patch/minor/major) or choose specific version # 3. The workflow creates a release with attached artifacts # # Documentation asset URL: # https://forge.eblu.me/eblume/blumeops/releases/download//docs-.tar.gz name: Build BlumeOps 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 echo "Fetching latest release..." LATEST=$(curl -s "https://forge.eblu.me/api/v1/repos/eblume/blumeops/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 # Validate format 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 "https://forge.eblu.me/api/v1/repos/eblume/blumeops/releases/tags/$VERSION" > /dev/null 2>&1; then echo "Error: Release $VERSION already exists" echo "See: https://forge.eblu.me/eblume/blumeops/releases/tag/$VERSION" exit 1 fi echo "version=$VERSION" >> "$GITHUB_OUTPUT" echo "Building BlumeOps 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 }}" # Run towncrier on the runner so that CHANGELOG.md updates and # fragment deletions appear in the working tree for both the Quartz # build (next step) and the git commit step. # Check if there are any changelog fragments 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 to include in release body 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..." # Towncrier already ran on the runner above, so the working tree # has an up-to-date CHANGELOG.md. build-docs now only runs the # Quartz static site build (no towncrier). 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 }}" echo "Creating release $VERSION..." # Build release body with changelog if available { echo "BlumeOps 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\` and configure the quartz container with:" echo "" echo "\`\`\`" echo "DOCS_RELEASE_URL=https://forge.eblu.me/eblume/blumeops/releases/download/$VERSION/$TARBALL" echo "\`\`\`" } > /tmp/release_body.txt # Use jq to properly escape the body for JSON RELEASE_DATA=$(jq -n \ --arg tag "$VERSION" \ --arg name "BlumeOps $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" \ "https://forge.eblu.me/api/v1/repos/eblume/blumeops/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..." UPLOAD_RESPONSE=$(curl -s \ -X POST \ -H "Content-Type: application/gzip" \ -H "Authorization: token $GITHUB_TOKEN" \ --data-binary "@$TARBALL" \ "https://forge.eblu.me/api/v1/repos/eblume/blumeops/releases/$RELEASE_ID/assets?name=$TARBALL") echo "Upload Response: $UPLOAD_RESPONSE" echo "" echo "Release created successfully!" - name: Update docs deployment run: | VERSION="${{ steps.version.outputs.version }}" TARBALL="docs-${VERSION}.tar.gz" DEPLOYMENT_FILE="argocd/manifests/docs/deployment.yaml" RELEASE_URL="https://forge.eblu.me/eblume/blumeops/releases/download/${VERSION}/${TARBALL}" echo "Updating $DEPLOYMENT_FILE with new release URL..." yq -i "(.spec.template.spec.containers[0].env[] | select(.name == \"DOCS_RELEASE_URL\")).value = \"${RELEASE_URL}\"" "$DEPLOYMENT_FILE" echo "Updated deployment:" grep -A1 "DOCS_RELEASE_URL" "$DEPLOYMENT_FILE" - name: Commit release changes env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | VERSION="${{ steps.version.outputs.version }}" CHANGELOG_UPDATED="${{ steps.changelog.outputs.changelog_updated }}" # Configure git git config user.name "Forgejo Actions" git config user.email "actions@forge.ops.eblu.me" # Stage deployment changes git add argocd/manifests/docs/deployment.yaml # Stage changelog changes if updated if [ "$CHANGELOG_UPDATED" = "true" ]; then git add CHANGELOG.md docs/changelog.d/ fi # Check if there are changes to commit if git diff --cached --quiet; then echo "No changes to commit" else git commit -m "Update docs release to $VERSION $([ "$CHANGELOG_UPDATED" = "true" ] && echo "- Built changelog from towncrier fragments") [skip ci]" # Push to main git push origin HEAD:main echo "Changes committed and pushed" fi - name: Deploy docs env: ARGOCD_AUTH_TOKEN: ${{ secrets.ARGOCD_AUTH_TOKEN }} run: | echo "Syncing docs app via ArgoCD..." # Sync docs app (uses ARGOCD_AUTH_TOKEN env var for auth) argocd app sync docs \ --server argocd.ops.eblu.me \ --grpc-web \ --prune # Wait for sync to complete argocd app wait docs \ --server argocd.ops.eblu.me \ --grpc-web \ --timeout 120 echo "Docs app synced successfully!" - name: Purge Fly.io proxy cache env: FLY_API_TOKEN: ${{ secrets.FLY_DEPLOY_TOKEN }} run: | echo "Purging nginx cache on Fly.io proxy..." fly ssh console -a blumeops-proxy -C "sh -c 'rm -rf /tmp/cache && nginx -s reload'" echo "Cache purged" - name: Summary run: | VERSION="${{ steps.version.outputs.version }}" TARBALL="docs-${VERSION}.tar.gz" echo "================================================" echo "BlumeOps Release: $VERSION" echo "================================================" echo "" echo "Release URL:" echo " https://forge.eblu.me/eblume/blumeops/releases/tag/$VERSION" echo "" echo "Asset URL (for DOCS_RELEASE_URL ConfigMap):" echo " https://forge.eblu.me/eblume/blumeops/releases/download/$VERSION/$TARBALL"