From efdd56928505ac3482bd0d7a5424c37e26242035 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Wed, 4 Feb 2026 08:13:16 -0800 Subject: [PATCH] Improve build workflow with version bump selection and changelog in releases (#104) ## Summary - Add `version_type` choice input with options: BUMP_PATCH (default), BUMP_MINOR, BUMP_MAJOR, SPECIFIC_VERSION - Add optional `specific_version` input for explicit version selection - Include changelog content in Forgejo release body under "What's Changed" section - Move CHANGELOG.md to repository root (still copied into docs during Quartz build) - Add CHANGELOG link to docs index page - Update doc-links script to recognize build-time docs from repo root ## Changes **Workflow inputs:** - Previously: single optional `version` string input - Now: `version_type` choice dropdown (defaults to BUMP_PATCH) + optional `specific_version` for explicit versions **Release body:** - Previously: just asset download instructions - Now: includes "What's Changed" section with changelog entries for this release **CHANGELOG.md location:** - Previously: `docs/CHANGELOG.md` - Now: `CHANGELOG.md` (repo root), copied into docs content during build ## Deployment and Testing - [ ] Run build workflow with BUMP_PATCH (default) - [ ] Run build workflow with BUMP_MINOR - [ ] Verify changelog appears in release body - [ ] Verify docs site includes CHANGELOG page Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/104 --- .forgejo/workflows/build-blumeops.yaml | 144 +++++++++++++----- docs/CHANGELOG.md => CHANGELOG.md | 6 + ...ure-build-workflow-improvements.feature.md | 1 + ...ature-build-workflow-improvements.infra.md | 1 + ...eature-build-workflow-improvements.misc.md | 1 + docs/how-to/update-documentation.md | 4 +- docs/index.md | 1 + mise-tasks/doc-links | 8 + towncrier.toml | 2 +- 9 files changed, 129 insertions(+), 39 deletions(-) rename docs/CHANGELOG.md => CHANGELOG.md (99%) create mode 100644 docs/changelog.d/feature-build-workflow-improvements.feature.md create mode 100644 docs/changelog.d/feature-build-workflow-improvements.infra.md create mode 100644 docs/changelog.d/feature-build-workflow-improvements.misc.md diff --git a/.forgejo/workflows/build-blumeops.yaml b/.forgejo/workflows/build-blumeops.yaml index 6d8096e..5638ae5 100644 --- a/.forgejo/workflows/build-blumeops.yaml +++ b/.forgejo/workflows/build-blumeops.yaml @@ -7,7 +7,7 @@ # # Usage: # 1. Go to Actions > Build BlumeOps > Run workflow -# 2. Enter a version tag (e.g., v1.2.0) or leave empty to auto-increment patch +# 2. Select version bump type (patch/minor/major) or choose specific version # 3. The workflow creates a release with attached artifacts # # Documentation asset URL: @@ -18,8 +18,18 @@ name: Build BlumeOps on: workflow_dispatch: inputs: - version: - description: 'Version (e.g., v1.2.0) or empty to auto-increment patch (v_._.+1)' + 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 @@ -31,31 +41,58 @@ jobs: - name: Resolve version id: version run: | - INPUT_VERSION="${{ inputs.version }}" + VERSION_TYPE="${{ inputs.version_type }}" + SPECIFIC_VERSION="${{ inputs.specific_version }}" - if [ -n "$INPUT_VERSION" ]; then - # User provided a version - validate it - if [[ ! "$INPUT_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="$INPUT_VERSION" - echo "Using provided version: $VERSION" + # Fetch latest release + echo "Fetching latest release..." + LATEST=$(curl -s "https://forge.ops.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 - # Auto-increment patch version from latest release - echo "Fetching latest release..." - LATEST=$(curl -s "https://forge.ops.eblu.me/api/v1/repos/eblume/blumeops/releases/latest" | jq -r '.tag_name // empty' || true) - - if [ -z "$LATEST" ]; then - VERSION="v1.0.0" - echo "No previous releases found, starting at: $VERSION" - else - # Parse vX.Y.Z and increment patch - VERSION=$(echo "$LATEST" | awk -F. '{print $1"."$2"."$3+1}') - echo "Auto-incremented from $LATEST to: $VERSION" - fi + 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.ops.eblu.me/api/v1/repos/eblume/blumeops/releases/tags/$VERSION" > /dev/null 2>&1; then echo "Error: Release $VERSION already exists" @@ -73,6 +110,7 @@ jobs: fetch-depth: 0 - name: Build changelog + id: changelog run: | VERSION="${{ steps.version.outputs.version }}" @@ -83,11 +121,25 @@ jobs: 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 + # The section starts with "## [$VERSION]" and ends before the next "## [" or EOF + RELEASE_NOTES=$(awk -v ver="$VERSION" ' + /^## \[/ { + if (found) exit + if (index($0, "[" ver "]")) found=1 + } + found {print} + ' CHANGELOG.md | tail -n +2) + + # Save release notes to a file for later use (handles multiline content) + 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 - id: changelog - name: Build docs run: | @@ -110,6 +162,9 @@ jobs: rm -rf content cp -r "$GITHUB_WORKSPACE/docs" content + # Copy CHANGELOG.md from repo root into content so it's accessible in docs + cp "$GITHUB_WORKSPACE/CHANGELOG.md" content/ + # Build echo "Building static site..." npx quartz build @@ -128,20 +183,37 @@ jobs: run: | VERSION="${{ steps.version.outputs.version }}" TARBALL="docs-${VERSION}.tar.gz" + CHANGELOG_UPDATED="${{ steps.changelog.outputs.changelog_updated }}" echo "Creating release $VERSION..." - # Use Forgejo API to create release (requires authentication) - RELEASE_DATA=$(cat < /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 \ @@ -203,7 +275,7 @@ jobs: # Stage changelog changes if updated if [ "$CHANGELOG_UPDATED" = "true" ]; then - git add docs/CHANGELOG.md docs/changelog.d/ + git add CHANGELOG.md docs/changelog.d/ fi # Check if there are changes to commit diff --git a/docs/CHANGELOG.md b/CHANGELOG.md similarity index 99% rename from docs/CHANGELOG.md rename to CHANGELOG.md index 5c824fa..3c18c82 100644 --- a/docs/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +--- +title: changelog +tags: + - meta +--- + # Changelog All notable changes to BlumeOps are documented in this file. diff --git a/docs/changelog.d/feature-build-workflow-improvements.feature.md b/docs/changelog.d/feature-build-workflow-improvements.feature.md new file mode 100644 index 0000000..b451c6a --- /dev/null +++ b/docs/changelog.d/feature-build-workflow-improvements.feature.md @@ -0,0 +1 @@ +Build workflow now supports version bump selection (major/minor/patch) and includes changelog in release body diff --git a/docs/changelog.d/feature-build-workflow-improvements.infra.md b/docs/changelog.d/feature-build-workflow-improvements.infra.md new file mode 100644 index 0000000..9661fd6 --- /dev/null +++ b/docs/changelog.d/feature-build-workflow-improvements.infra.md @@ -0,0 +1 @@ +Move CHANGELOG.md to repository root (still included in docs build) diff --git a/docs/changelog.d/feature-build-workflow-improvements.misc.md b/docs/changelog.d/feature-build-workflow-improvements.misc.md new file mode 100644 index 0000000..0da27b5 --- /dev/null +++ b/docs/changelog.d/feature-build-workflow-improvements.misc.md @@ -0,0 +1 @@ +doc-links script now recognizes build-time docs from repo root (e.g., CHANGELOG.md) diff --git a/docs/how-to/update-documentation.md b/docs/how-to/update-documentation.md index be47f5e..872dedb 100644 --- a/docs/how-to/update-documentation.md +++ b/docs/how-to/update-documentation.md @@ -15,7 +15,7 @@ How to publish documentation changes to https://docs.ops.eblu.me. After merging documentation changes to main: 1. Go to **Actions** > **Build BlumeOps** > **Run workflow** -2. Enter a version (e.g., `v1.2.0`) or leave empty to auto-increment +2. Select version bump type (patch/minor/major) or enter a specific version 3. The workflow builds, releases, and deploys automatically Direct link: https://forge.ops.eblu.me/eblume/blumeops/actions?workflow=build-blumeops.yaml @@ -47,7 +47,7 @@ echo "Add new feature X" > docs/changelog.d/my-feature.feature.md echo "Fix bug Y" > docs/changelog.d/+fix-bug.bugfix.md ``` -Fragments are automatically collected into `docs/CHANGELOG.md` during release. +Fragments are automatically collected into `CHANGELOG.md` (at repo root) during release. **Fragment types:** | Type | Directory | Description | diff --git a/docs/index.md b/docs/index.md index 0e71774..2a2bec1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -12,3 +12,4 @@ Welcome to the BlumeOps documentation. - [[reference/index | Reference]] - Technical specifications and service details - [[how-to/index | How-to]] - Task-oriented instructions for common operations - [[explanation/index | Explanation]] - Understanding the "why" behind BlumeOps +- [[CHANGELOG]] - Release history and changes diff --git a/mise-tasks/doc-links b/mise-tasks/doc-links index 625c9ff..bdc0f2b 100755 --- a/mise-tasks/doc-links +++ b/mise-tasks/doc-links @@ -82,6 +82,14 @@ def main() -> int: else: ambiguous_filenames.add(filename) + # Special case: files at repo root that are copied into docs during build + # These are valid link targets even though they don't exist in docs/ + REPO_ROOT = DOCS_DIR.parent + BUILD_TIME_DOCS = ["CHANGELOG.md"] + for filename in BUILD_TIME_DOCS: + if (REPO_ROOT / filename).exists(): + valid_targets.add(Path(filename).stem) + # Collect all broken and ambiguous links broken_links: list[tuple[str, int, str]] = [] ambiguous_links: list[tuple[str, int, str, list[str]]] = [] diff --git a/towncrier.toml b/towncrier.toml index 6f2dba3..199f4ea 100644 --- a/towncrier.toml +++ b/towncrier.toml @@ -3,7 +3,7 @@ [tool.towncrier] directory = "docs/changelog.d" -filename = "docs/CHANGELOG.md" +filename = "CHANGELOG.md" package = "" title_format = "## [{version}] - {project_date}" issue_format = ""