175 lines
7.3 KiB
YAML
175 lines
7.3 KiB
YAML
name: Prepare Release
|
|
|
|
# Dispatch with a version number to open a "Release vX.Y.Z" PR containing the
|
|
# version bump (package.json, package-lock.json, AppSettings.php) and an
|
|
# AI-drafted CHANGELOG.md section. The regular CI suite runs on the PR as the
|
|
# final test gate; merging the PR triggers the Create Release workflow which
|
|
# tags, packages, and publishes.
|
|
#
|
|
# Requires two repo secrets:
|
|
# - RELEASE_TOKEN: fine-grained PAT with Contents + Pull requests read/write.
|
|
# PRs opened with the default GITHUB_TOKEN would not trigger CI workflows.
|
|
# - ANTHROPIC_API_KEY: Claude API key used to draft the changelog section.
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: 'New version number (e.g. 3.8.1)'
|
|
required: true
|
|
type: string
|
|
|
|
# Least privilege: branch push + PR creation use the RELEASE_TOKEN PAT, so the
|
|
# default token only needs read access (checkout, changelog).
|
|
permissions:
|
|
contents: read
|
|
pull-requests: read
|
|
|
|
jobs:
|
|
prepare:
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
VERSION: ${{ inputs.version }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Validate version
|
|
run: |
|
|
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
echo "::error::Version '$VERSION' is not a valid X.Y.Z version number"
|
|
exit 1
|
|
fi
|
|
CURRENT=$(make get-version)
|
|
if [ "$VERSION" = "$CURRENT" ]; then
|
|
echo "::error::Version $VERSION is already the current version"
|
|
exit 1
|
|
fi
|
|
if git rev-parse -q --verify "refs/tags/v$VERSION" > /dev/null; then
|
|
echo "::error::Tag v$VERSION already exists"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Bump version files
|
|
run: |
|
|
npm version "$VERSION" --no-git-tag-version
|
|
sed -i "s/appVersion = '[^']*'/appVersion = '$VERSION'/" app/Core/Configuration/AppSettings.php
|
|
echo "Bumped to $(make get-version)"
|
|
|
|
- name: Collect history since last release
|
|
run: |
|
|
LAST_TAG=$(git describe --tags --abbrev=0 --match 'v*')
|
|
echo "Generating changelog for $LAST_TAG..HEAD"
|
|
git log "$LAST_TAG"..HEAD --no-merges --pretty='- %s' > "$RUNNER_TEMP/commits.md"
|
|
wc -l "$RUNNER_TEMP/commits.md"
|
|
|
|
- name: Build changelog prompt
|
|
run: |
|
|
# Most recent CHANGELOG section doubles as the style example
|
|
awk '/^# Version:/{n++} n==1' CHANGELOG.md > "$RUNNER_TEMP/style-example.md"
|
|
|
|
cat > "$RUNNER_TEMP/system-prompt.md" << 'SYSEOF'
|
|
You are the release manager for Leantime, an open source project management
|
|
system. You write the CHANGELOG.md section for a new release based on the
|
|
git commit subjects since the last release.
|
|
|
|
Format rules:
|
|
- Start with exactly: # Version: <version>
|
|
- Use these sections, in this order, and ONLY if they have content:
|
|
## Highlights, ## New Features, ## Bug Fixes, ## Improvements, ## Security, ## Localization, ## Dependency Updates
|
|
- Highlights: at most 1-3 entries, only for genuinely notable user-facing
|
|
features (### subheading + a short paragraph). Most releases have none -
|
|
omit the section rather than inflate minor work.
|
|
- All other entries are single bullets: **Short Title** - one-sentence
|
|
user-facing description ending with the PR reference(s), e.g. (#1234)
|
|
- Rewrite commit subjects into user-facing language; never copy
|
|
conventional-commit prefixes like fix(scope): into the output.
|
|
- Merge multiple commits about the same change into one bullet with all
|
|
PR references.
|
|
- Group dependency bumps into ## Dependency Updates as terse bullets.
|
|
- Skip pure chores with no user impact (CI tweaks, code style, review
|
|
follow-up commits that have no PR reference).
|
|
- Output raw markdown only - no surrounding code fences, no commentary.
|
|
|
|
Here is the previous release's section as a style example:
|
|
|
|
SYSEOF
|
|
cat "$RUNNER_TEMP/style-example.md" >> "$RUNNER_TEMP/system-prompt.md"
|
|
|
|
{
|
|
echo "Write the CHANGELOG section for version $VERSION based on these commits:"
|
|
echo
|
|
cat "$RUNNER_TEMP/commits.md"
|
|
} > "$RUNNER_TEMP/prompt.md"
|
|
|
|
- name: Generate changelog section
|
|
env:
|
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
run: |
|
|
if [ -z "$ANTHROPIC_API_KEY" ]; then
|
|
echo "::error::ANTHROPIC_API_KEY secret is not set"
|
|
exit 1
|
|
fi
|
|
|
|
jq -n \
|
|
--rawfile system "$RUNNER_TEMP/system-prompt.md" \
|
|
--rawfile prompt "$RUNNER_TEMP/prompt.md" \
|
|
'{
|
|
model: "claude-opus-4-8",
|
|
max_tokens: 8000,
|
|
thinking: {type: "adaptive"},
|
|
system: $system,
|
|
messages: [{role: "user", content: $prompt}]
|
|
}' > "$RUNNER_TEMP/request.json"
|
|
|
|
if ! curl -sS --fail-with-body --max-time 600 https://api.anthropic.com/v1/messages \
|
|
-H "x-api-key: $ANTHROPIC_API_KEY" \
|
|
-H "anthropic-version: 2023-06-01" \
|
|
-H "content-type: application/json" \
|
|
-d @"$RUNNER_TEMP/request.json" > "$RUNNER_TEMP/response.json"; then
|
|
echo "::error::Anthropic API request failed"
|
|
cat "$RUNNER_TEMP/response.json"
|
|
exit 1
|
|
fi
|
|
|
|
# Adaptive thinking responses interleave thinking blocks - keep text blocks only
|
|
jq -r '[.content[] | select(.type == "text") | .text] | join("\n")' \
|
|
"$RUNNER_TEMP/response.json" > "$RUNNER_TEMP/ai-response.md"
|
|
|
|
if ! [ -s "$RUNNER_TEMP/ai-response.md" ]; then
|
|
echo "::error::Empty changelog from the API (stop_reason: $(jq -r '.stop_reason' "$RUNNER_TEMP/response.json"))"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Prepend section to CHANGELOG.md
|
|
run: |
|
|
sed '/^```/d' "$RUNNER_TEMP/ai-response.md" > "$RUNNER_TEMP/new-section.md"
|
|
if ! head -1 "$RUNNER_TEMP/new-section.md" | grep -q "^# Version: $VERSION"; then
|
|
printf '# Version: %s\n\n' "$VERSION" | cat - "$RUNNER_TEMP/new-section.md" > "$RUNNER_TEMP/new-section.tmp"
|
|
mv "$RUNNER_TEMP/new-section.tmp" "$RUNNER_TEMP/new-section.md"
|
|
fi
|
|
{ cat "$RUNNER_TEMP/new-section.md"; echo; echo '---'; echo; cat CHANGELOG.md; } > "$RUNNER_TEMP/changelog.md"
|
|
mv "$RUNNER_TEMP/changelog.md" CHANGELOG.md
|
|
|
|
{
|
|
echo "Automated release PR. Review and edit the changelog below (it ships as CHANGELOG.md and as the GitHub release notes), wait for CI, then merge - the release publishes automatically."
|
|
echo
|
|
echo '---'
|
|
echo
|
|
cat "$RUNNER_TEMP/new-section.md"
|
|
} > "$RUNNER_TEMP/pr-body.md"
|
|
|
|
- name: Open release PR
|
|
uses: peter-evans/create-pull-request@v7
|
|
with:
|
|
token: ${{ secrets.RELEASE_TOKEN }}
|
|
commit-message: 'release: bump version to ${{ inputs.version }} + changelog'
|
|
branch: release/v${{ inputs.version }}
|
|
delete-branch: true
|
|
base: master
|
|
title: 'Release v${{ inputs.version }}'
|
|
labels: release
|
|
body-path: ${{ runner.temp }}/pr-body.md
|