Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
/bin
!/bin/validate-json
!/bin/update-changelog.sh
!/bin/prepare-release.sh
!/bin/extract-release-notes.sh
coverage
.buildpath
.project
Expand Down
58 changes: 58 additions & 0 deletions bin/extract-release-notes.sh
Original file line number Diff line number Diff line change
@@ -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 <version>"
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
95 changes: 95 additions & 0 deletions bin/prepare-release.sh
Original file line number Diff line number Diff line change
@@ -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 <version>"
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"