-
Notifications
You must be signed in to change notification settings - Fork 6
Auto semver and release notes generation for releases #696
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+287
−7
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
2d5c5f5
add automation for deciding next version and release notes using AI
sami-alajrami 8058dd4
improve prompt and limit releasing to main branch
sami-alajrami 866b0f7
add cleanup of temp release notes files
sami-alajrami 8e40d21
first round of improvments
sami-alajrami fdff8ad
apply second round of reviews
sami-alajrami 7225f24
clean dead comments
sami-alajrami File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| #!/usr/bin/env bash | ||
| # Interactive step after suggest-version-ai: show version and release notes, | ||
| # let user edit notes, then confirm before creating tag and pushing. | ||
| # Called from Make when running `make release` (no tag). | ||
| # Requires: dist/suggested_version and dist/release_notes.md exist. | ||
|
|
||
| set -e | ||
|
|
||
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" | ||
| cd "$REPO_ROOT" | ||
|
|
||
| SUGGESTED_VERSION_FILE="dist/suggested_version" | ||
| RELEASE_NOTES_FILE="dist/release_notes.md" | ||
|
|
||
| if [ ! -f "$SUGGESTED_VERSION_FILE" ] || [ ! -f "$RELEASE_NOTES_FILE" ]; then | ||
| echo "Missing $SUGGESTED_VERSION_FILE or $RELEASE_NOTES_FILE. Run suggest-version-ai first." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| VER=$(cat "$SUGGESTED_VERSION_FILE") | ||
| if [ -z "$VER" ]; then | ||
| echo "Suggested version is empty. Run suggest-version-ai or use: make release tag=vX.Y.Z" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "Suggested tag: $VER" | ||
| echo "" | ||
| echo "Release notes ($RELEASE_NOTES_FILE):" | ||
| echo "---" | ||
| cat "$RELEASE_NOTES_FILE" | ||
| echo "---" | ||
| echo "" | ||
|
|
||
| # Let user edit release notes | ||
| read -r -p "Edit release notes? [y/N] " edit_notes | ||
| case "$edit_notes" in | ||
| y|Y) "${EDITOR:-vi}" "$RELEASE_NOTES_FILE" ;; | ||
| *) ;; | ||
| esac | ||
|
|
||
| echo "" | ||
| read -r -p "Create tag $VER and push? [y/N] " confirm | ||
| case "$confirm" in | ||
| y|Y) ;; | ||
| *) echo "Aborted. To release later run: make release tag=$VER"; exit 0 ;; | ||
| esac | ||
|
|
||
| git remote update | ||
| if ! git status -uno | grep -q "Your branch is up to date"; then | ||
| echo "ERROR: your branch is NOT up to date with remote" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| git tag -a "$VER" -F "$RELEASE_NOTES_FILE" | ||
| git push origin "$VER" | ||
| echo "Pushed tag $VER. Release workflow will run on GitHub." |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,162 @@ | ||
| #!/usr/bin/env bash | ||
| # Suggest next semver and changelog by sending the git diff to Claude. | ||
| # Does not rely on commit messages. Changelog is suitable for GoReleaser --release-notes. | ||
| # | ||
| # Auth (first non-empty wins): | ||
| # - ANTHROPIC_API_KEY: call Claude directly. | ||
| # - OP_ANTHROPIC_API_KEY_REF: 1Password reference (default below; override if your item path differs). | ||
| # | ||
| # Optional: CLAUDE_MODEL (default: claude-sonnet-4-6) — e.g. claude-opus-4-6. | ||
| # | ||
| # Requires: curl, jq; for 1Password: op CLI | ||
| # Usage: bin/suggest-version-ai.sh [base_ref] [-o release_notes.md] | ||
| # base_ref defaults to the latest git tag. | ||
| # -o FILE write changelog markdown to FILE (default: dist/release_notes.md) | ||
| # | ||
| # Output: bump (major|minor|patch), next_version (e.g. v1.3.0), and changelog file. | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| BASE_REF="" | ||
| RELEASE_NOTES_FILE="dist/release_notes.md" | ||
| while [[ $# -gt 0 ]]; do | ||
| case "$1" in | ||
| -o) RELEASE_NOTES_FILE="$2"; shift 2 ;; | ||
| *) BASE_REF="$1"; shift ;; | ||
| esac | ||
| done | ||
| BASE_REF="${BASE_REF:-$(git describe --tags --abbrev=0 2>/dev/null)}" | ||
| SUGGESTED_VERSION_FILE="$(dirname "$RELEASE_NOTES_FILE")/suggested_version" | ||
|
|
||
| if [ -z "$BASE_REF" ]; then | ||
| echo "ERROR: No base ref. Pass a tag or branch, or create a tag first." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Cap diff size to stay within context | ||
| MAX_DIFF_CHARS=50000 | ||
| DIFF="$(git diff "$BASE_REF"..HEAD 2>/dev/null | head -c "$MAX_DIFF_CHARS")" | ||
|
|
||
| # Get API key from 1Password if not set (default ref; override with OP_ANTHROPIC_API_KEY_REF) | ||
| OP_ANTHROPIC_API_KEY_REF="${OP_ANTHROPIC_API_KEY_REF:-op://Shared/Anthropic API Key/credential}" | ||
| if [ -z "${ANTHROPIC_API_KEY:-}" ]; then | ||
| if command -v op >/dev/null 2>&1; then | ||
| ANTHROPIC_API_KEY=$(op read "$OP_ANTHROPIC_API_KEY_REF" 2>/dev/null) || true | ||
| fi | ||
| fi | ||
| if [ -z "${ANTHROPIC_API_KEY:-}" ]; then | ||
| echo "ERROR: Set ANTHROPIC_API_KEY or OP_ANTHROPIC_API_KEY_REF (1Password)." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Remove stale outputs from a previous run so a failure partway through doesn't mislead the next invocation. | ||
| trap 'rm -f "$RELEASE_NOTES_FILE" "$SUGGESTED_VERSION_FILE"' EXIT | ||
|
|
||
| if [ -z "$DIFF" ]; then | ||
| echo "No changes since $BASE_REF. Bump: patch (no change)." >&2 | ||
| CHANGELOG="No code changes since $BASE_REF." | ||
| mkdir -p "$(dirname "$RELEASE_NOTES_FILE")" | ||
| echo "$CHANGELOG" > "$RELEASE_NOTES_FILE" | ||
| echo "patch" | ||
| CURRENT="${BASE_REF#v}" | ||
| if [[ "$CURRENT" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then | ||
| MAJOR="${CURRENT%%.*}"; REST="${CURRENT#*.}"; MINOR="${REST%%.*}"; PATCH="${REST#*.}"; PATCH="${PATCH%%[-+]*}" | ||
| NEXT="v${MAJOR}.${MINOR}.$((PATCH+1))" | ||
| if [ -n "$(git tag -l "$NEXT")" ]; then | ||
| echo "ERROR: tag $NEXT already exists. Push it or use: make release tag=$NEXT" >&2 | ||
| exit 1 | ||
| fi | ||
| echo "$NEXT" > "$SUGGESTED_VERSION_FILE" | ||
| echo "$NEXT" | ||
| fi | ||
| trap - EXIT | ||
| exit 0 | ||
| fi | ||
|
|
||
| PROMPT="You are a release engineer. Given the following git diff for a CLI application (Kosli CLI), do two things. | ||
|
|
||
| Scope: Consider ONLY changes to the CLI itself—i.e. code under cmd/ and internal/ that affects user-facing commands, flags, and behavior. IGNORE all other changes when deciding the version and when writing the changelog: | ||
| - Ignore: documentation (docs*, *.md), Helm charts (charts/), CI/workflows (.github/), scripts (bin/, scripts/), tests (*_test.go, testdata/), Makefile, config files, and any other non-CLI code. | ||
| - If the diff contains only ignored changes, recommend a patch bump and write a single short line for the changelog (e.g. \"No user-facing CLI changes.\"). | ||
|
|
||
| 1) Suggest the semantic version bump (based only on CLI changes): | ||
| - major: Breaking changes (removed/renamed commands or flags, changed default behavior). | ||
| - minor: New commands, flags, subcommands, or features. | ||
| - patch: Bug fixes, refactors, internal or dependency updates; or no user-facing CLI changes. | ||
|
|
||
| 2) Write a short changelog in markdown for the GitHub release body. Include only user-facing CLI changes. Use bullet points; be concise; no preamble. | ||
| - Structure the changelog with section headers (e.g. \"# Breaking changes\", \"# New features\", \"# Bug fixes\" or \"# Improvements\") and list items under each header. Use only headers that have at least one change—omit any section that would be empty. | ||
| - Do not write placeholder lines under any header (no \"No other changes\", \"No user-facing CLI changes in this release\", or similar). If there are no CLI changes at all, output a single short line only (no headers). | ||
|
|
||
| Reply in this exact format (no other text before or after): | ||
| BUMP: major|minor|patch | ||
| ---CHANGELOG--- | ||
| <markdown changelog here>" | ||
|
|
||
| CLAUDE_MODEL="${CLAUDE_MODEL:-claude-sonnet-4-6}" | ||
| DIFF_FILE=$(mktemp) | ||
| trap 'rm -f "$DIFF_FILE" "$RELEASE_NOTES_FILE" "$SUGGESTED_VERSION_FILE"' EXIT | ||
| printf '%s' "$DIFF" > "$DIFF_FILE" | ||
| BODY=$(jq -n \ | ||
| --arg model "$CLAUDE_MODEL" \ | ||
| --arg prompt "$PROMPT" \ | ||
| --rawfile diff "$DIFF_FILE" \ | ||
| '{model: $model, max_tokens: 1024, messages: [{role: "user", content: ($prompt + "\n\n--- diff ---\n\n" + $diff)}]}') | ||
|
|
||
| RESPONSE=$(curl -s -S -X POST "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 "$BODY") | ||
|
|
||
| CONTENT=$(echo "$RESPONSE" | jq -r '.content[0].text // empty') | ||
| if [ -z "$CONTENT" ]; then | ||
| echo "ERROR: Anthropic API failed or returned no content. Response:" >&2 | ||
| echo "$RESPONSE" | jq . >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| BUMP=$(echo "$CONTENT" | tr '[:upper:]' '[:lower:]' | grep -oE 'major|minor|patch' | head -1) | ||
| case "$BUMP" in | ||
| major|minor|patch) ;; | ||
| *) | ||
| echo "WARN: Could not parse bump (got: $CONTENT). Defaulting to patch." >&2 | ||
| BUMP=patch | ||
| ;; | ||
| esac | ||
|
|
||
| CHANGELOG_MARKER='---CHANGELOG---' | ||
| if echo "$CONTENT" | grep -qF -- "$CHANGELOG_MARKER"; then | ||
| CHANGELOG=$(echo "$CONTENT" | sed -n "/${CHANGELOG_MARKER}/,\$ p" | tail -n +2) | ||
| else | ||
| CHANGELOG=$(echo "$CONTENT" | sed -n '2,$ p') | ||
| fi | ||
| mkdir -p "$(dirname "$RELEASE_NOTES_FILE")" | ||
| echo "$CHANGELOG" > "$RELEASE_NOTES_FILE" | ||
|
|
||
| # Compute next version from current tag | ||
| CURRENT="${BASE_REF#v}" | ||
| if [[ "$CURRENT" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then | ||
| MAJOR="${CURRENT%%.*}"; REST="${CURRENT#*.}"; MINOR="${REST%%.*}"; PATCH="${REST#*.}"; PATCH="${PATCH%%[-+]*}" | ||
| case "$BUMP" in | ||
| major) NEXT="v$((MAJOR+1)).0.0" ;; | ||
| minor) NEXT="v${MAJOR}.$((MINOR+1)).0" ;; | ||
| patch) NEXT="v${MAJOR}.${MINOR}.$((PATCH+1))" ;; | ||
| esac | ||
| else | ||
| NEXT="" | ||
| fi | ||
|
|
||
| if [ -n "$NEXT" ]; then | ||
| if [ -n "$(git tag -l "$NEXT")" ]; then | ||
| echo "ERROR: tag $NEXT already exists. Push it or use: make release tag=$NEXT" >&2 | ||
| exit 1 | ||
| fi | ||
| echo "$NEXT" > "$SUGGESTED_VERSION_FILE" | ||
| fi | ||
| echo "Suggested bump: $BUMP (from diff since $BASE_REF)" >&2 | ||
| echo "Next version: $NEXT" >&2 | ||
| echo "Changelog: $RELEASE_NOTES_FILE" >&2 | ||
| trap - EXIT | ||
| echo "$BUMP" | ||
| echo "$NEXT" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.