diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..4307a8b1 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,98 @@ +# This workflow automates the release process for the project. +# +# Usage (for maintainers): +# 1. Go to Actions -> Create Release -> Run workflow +# 2. Enter the version number (e.g., 6.7.0) +# 3. The workflow will: +# - Move unreleased changes in CHANGELOG.md to a new version section +# - Create a commit with the updated changelog +# - Create and push a git tag (e.g., v6.7.0) +# - Create a GitHub release with the changelog as release notes +# +# Prerequisites: +# - All changes for the release must be in the [Unreleased] section of CHANGELOG.md +# - The version number must follow semantic versioning (X.Y.Z) + +name: "Create Release" + +on: + workflow_dispatch: + inputs: + version: + description: 'Version number (e.g., 6.7.0)' + required: true + type: string + +jobs: + create-release: + name: "Create Release" + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: "Checkout" + uses: "actions/checkout@v4" + with: + ref: main + fetch-depth: 0 + + - name: "Validate version format" + run: | + VERSION="${{ inputs.version }}" + if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Version must be in format X.Y.Z (e.g., 6.7.0)" + exit 1 + fi + echo "Version format is valid: $VERSION" + + - name: "Update CHANGELOG.md" + run: | + bash bin/prepare-release.sh "${{ inputs.version }}" + + - name: "Extract release notes" + id: release_notes + run: | + RELEASE_NOTES=$(bash bin/extract-release-notes.sh "${{ inputs.version }}") + echo "Release notes extracted successfully" + + # Save to a file for the release step + echo "$RELEASE_NOTES" > /tmp/release-notes.md + + # Also output for verification + echo "Release notes:" + cat /tmp/release-notes.md + + - name: "Commit changelog changes" + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + # Check if there are changes to commit + if git diff --quiet CHANGELOG.md; then + echo "Error: No changes made to CHANGELOG.md" + exit 1 + fi + + git add CHANGELOG.md + git commit -m "docs: Prepare release ${{ inputs.version }}" + git push origin main + + - name: "Create and push tag" + run: | + # Check if tag already exists + if git rev-parse "v${{ inputs.version }}" >/dev/null 2>&1; then + echo "Error: Tag v${{ inputs.version }} already exists" + exit 1 + fi + + git tag -a "v${{ inputs.version }}" -m "Release ${{ inputs.version }}" + git push origin "v${{ inputs.version }}" + + - name: "Create GitHub Release" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release create "v${{ inputs.version }}" \ + --title "Release ${{ inputs.version }}" \ + --notes-file /tmp/release-notes.md diff --git a/.gitignore b/.gitignore index a3835319..04b69a41 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ /bin !/bin/validate-json !/bin/update-changelog.sh +!/bin/prepare-release.sh +!/bin/extract-release-notes.sh coverage .buildpath .project diff --git a/bin/extract-release-notes.sh b/bin/extract-release-notes.sh new file mode 100755 index 00000000..b82b777d --- /dev/null +++ b/bin/extract-release-notes.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +set -e + +# Script to extract release notes for a specific version from CHANGELOG.md +# Usage: ./bin/extract-release-notes.sh "6.7.0" +# +# Arguments: +# $1 - Version number (e.g., "6.7.0") +# +# This script extracts all the content between the specified version header +# and the next version header + +if [ $# -lt 1 ]; then + echo "Usage: $0 " + echo "" + echo "Example: $0 '6.7.0'" + exit 1 +fi + +VERSION="$1" + +# Check if CHANGELOG.md exists +if [ ! -f CHANGELOG.md ]; then + echo "Error: CHANGELOG.md not found in current directory" + exit 1 +fi + +# Use awk to extract the release notes for the specified version +awk -v version="$VERSION" ' +BEGIN { in_version = 0; found = 0 } + +# Match the version header +$0 ~ "^## \\[" version "\\]" { + in_version = 1 + found = 1 + next +} + +# Match any other version header +/^## \[/ { + if (in_version) { + exit + } + next +} + +# Print content when in the correct version section +in_version { + print +} + +END { + if (!found) { + print "Error: Version " version " not found in CHANGELOG.md" > "/dev/stderr" + exit 1 + } +} +' CHANGELOG.md diff --git a/bin/prepare-release.sh b/bin/prepare-release.sh new file mode 100755 index 00000000..dc9052de --- /dev/null +++ b/bin/prepare-release.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash +set -e + +# Script to prepare a release by updating CHANGELOG.md +# Usage: ./bin/prepare-release.sh "6.7.0" +# +# Arguments: +# $1 - Version number (e.g., "6.7.0") +# +# This script: +# 1. Moves all entries from [Unreleased] to a new version section +# 2. Adds the release date +# 3. Leaves an empty [Unreleased] section + +if [ $# -lt 1 ]; then + echo "Usage: $0 " + echo "" + echo "Example: $0 '6.7.0'" + exit 1 +fi + +VERSION="$1" +RELEASE_DATE=$(date +%Y-%m-%d) + +echo "Preparing release for version: $VERSION" +echo "Release date: $RELEASE_DATE" + +# Check if CHANGELOG.md exists +if [ ! -f CHANGELOG.md ]; then + echo "Error: CHANGELOG.md not found in current directory" + exit 1 +fi + +# Check if there is content in the Unreleased section +# Extract content between [Unreleased] and the next version header +UNRELEASED_CONTENT=$(awk '/^## \[Unreleased\]/ {flag=1; next} /^## \[/ {flag=0} flag' CHANGELOG.md) +if ! echo "$UNRELEASED_CONTENT" | grep -q "^### "; then + echo "Error: No changes found in [Unreleased] section" + exit 1 +fi + +# Use awk to process the changelog +awk -v version="$VERSION" -v date="$RELEASE_DATE" ' +BEGIN { + in_unreleased = 0 + printed_unreleased = 0 + unreleased_content = "" +} + +# Match the Unreleased header +/^## \[Unreleased\]/ { + print $0 + printed_unreleased = 1 + in_unreleased = 1 + next +} + +# Match any other version header (## [X.X.X]) +/^## \[/ { + if (in_unreleased) { + # We are leaving the unreleased section + # Print the new version with the unreleased content + print "" + print "## [" version "] - " date + print unreleased_content + in_unreleased = 0 + } + print + next +} + +# Collect content from unreleased section +in_unreleased { + if (unreleased_content != "") { + unreleased_content = unreleased_content "\n" $0 + } else { + unreleased_content = $0 + } + next +} + +# Print all other lines +{ print } +' CHANGELOG.md > CHANGELOG.md.tmp + +if [ ! -s CHANGELOG.md.tmp ]; then + echo "Error: Failed to update CHANGELOG.md" + rm -f CHANGELOG.md.tmp + exit 1 +fi + +mv CHANGELOG.md.tmp CHANGELOG.md + +echo "CHANGELOG.md updated successfully" +echo "Created release section for version $VERSION"