diff --git a/.github/scripts/generate-changelog.mjs b/.github/scripts/generate-changelog.mjs new file mode 100644 index 0000000..6f9181c --- /dev/null +++ b/.github/scripts/generate-changelog.mjs @@ -0,0 +1,89 @@ +#!/usr/bin/env node +/** + * Generate a changelog entry and determine version bump using Claude. + */ + +import { appendFileSync, writeFileSync } from "fs"; + +const apiKey = process.env.ANTHROPIC_API_KEY; +if (!apiKey) { + console.error("Error: ANTHROPIC_API_KEY not set"); + process.exit(1); +} + +const oldVersion = process.env.OLD_VERSION || "0.0.0"; +const commits = process.env.COMMITS || ""; +const diffstat = process.env.DIFFSTAT || ""; +const today = new Date().toISOString().split("T")[0]; + +const prompt = `You are generating a release changelog for @lightsparkdev/origin, a React component library and design system. + +Current version: ${oldVersion} +Diff stat: ${diffstat} + +Commits since last release: +${commits} + +Tasks: +1. Determine if this is a "minor" or "patch" release. Use minor if there are new components, new features, or new public API surface. Use patch for bug fixes, docs, internal refactors, CI changes, and dependency updates. Never use major. +2. Write a concise changelog entry that summarizes ONLY changes that impact consumers of the package (new components, API changes, bug fixes, style changes). Omit CI, docs, internal tooling, and repo maintenance. + +Respond in EXACTLY this format with no other text: + +VERSION_BUMP: patch +CHANGELOG: +## ${oldVersion} → X.Y.Z (${today}) + +- First user-facing change +- Second user-facing change + +Replace X.Y.Z with the actual new version number. If there are no user-facing changes, write a single line: "- Internal maintenance release (no user-facing changes)".`; + +const resp = await fetch("https://api.anthropic.com/v1/messages", { + method: "POST", + headers: { + "content-type": "application/json", + "x-api-key": apiKey, + "anthropic-version": "2023-06-01", + }, + body: JSON.stringify({ + model: "claude-sonnet-4-6", + max_tokens: 1024, + messages: [{ role: "user", content: prompt }], + }), +}); + +if (!resp.ok) { + const body = await resp.text(); + console.error(`API error ${resp.status}: ${body}`); + process.exit(1); +} + +const result = await resp.json(); +const aiOutput = result.content[0].text; +console.error(aiOutput); + +// Parse version bump +let versionBump = "patch"; +for (const line of aiOutput.split("\n")) { + if (line.startsWith("VERSION_BUMP:")) { + const parsed = line.split(":")[1].trim(); + if (parsed === "minor" || parsed === "patch") { + versionBump = parsed; + } + break; + } +} + +// Parse changelog (everything after CHANGELOG: line) +const lines = aiOutput.split("\n"); +const changelogStart = lines.findIndex((l) => l.startsWith("CHANGELOG:")); +const changelog = + changelogStart >= 0 ? lines.slice(changelogStart + 1).join("\n").trim() : ""; + +// Write outputs +appendFileSync(process.env.GITHUB_OUTPUT, `version_bump=${versionBump}\n`); +writeFileSync("/tmp/changelog_entry.md", changelog + "\n"); + +console.log(`--- AI determined: ${versionBump} ---`); +console.log(changelog); diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4e7183b..5f345b7 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -2,6 +2,11 @@ name: "Create Release PR" on: workflow_dispatch: + # Temporary: run on PRs that modify this workflow for validation + pull_request: + paths: + - ".github/workflows/release.yaml" + - ".github/scripts/generate-changelog.mjs" jobs: create-release-pr: @@ -29,8 +34,15 @@ jobs: - name: Collect changes since last release id: changes run: | + # Ensure tags are fetched from the upstream repo + git fetch origin --tags + OLD_VERSION=$(node -p "require('./package.json').version") echo "old_version=$OLD_VERSION" >> "$GITHUB_OUTPUT" + echo "Current version: $OLD_VERSION" + echo "Tag exists: $(git tag -l v${OLD_VERSION})" + echo "Commits since tag:" + git log v${OLD_VERSION}..HEAD --oneline | head -10 || echo "(none)" # Collect PR merge commit messages since last tag MERGES=$(git log v${OLD_VERSION}..HEAD --merges --pretty=format:"%s" || true) @@ -43,103 +55,29 @@ jobs: # Also collect the diff stat for context DIFFSTAT=$(git diff v${OLD_VERSION}..HEAD --stat | tail -1) - # Write to files for the next step - echo "$MERGES" > /tmp/commit_messages.txt - echo "$DIFFSTAT" > /tmp/diffstat.txt + # Write to env files for the next step + { + echo "COMMITS<> "$GITHUB_ENV" + + { + echo "DIFFSTAT<> "$GITHUB_ENV" - name: Determine version bump and generate changelog id: ai env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - run: | - OLD_VERSION="${{ steps.changes.outputs.old_version }}" - COMMITS=$(cat /tmp/commit_messages.txt) - DIFFSTAT=$(cat /tmp/diffstat.txt) - DATE=$(date +%Y-%m-%d) - - # Build the prompt - cat > /tmp/prompt.json << 'PROMPT_EOF' - { - "model": "claude-sonnet-4-5-20250514", - "max_tokens": 1024, - "messages": [ - { - "role": "user", - "content": "COMMITS_PLACEHOLDER" - } - ] - } - PROMPT_EOF - - # Inject the actual prompt content with proper escaping - PROMPT_TEXT=$(cat <> "$GITHUB_OUTPUT" - - # Write changelog to file (multi-line safe) - echo "$CHANGELOG" > /tmp/changelog_entry.md - - echo "--- AI determined: $VERSION_BUMP ---" - echo "$CHANGELOG" + OLD_VERSION: ${{ steps.changes.outputs.old_version }} + run: node .github/scripts/generate-changelog.mjs + # Skip the remaining steps on PR runs (just validate the AI step works) - name: Bump version + if: github.event_name == 'workflow_dispatch' id: bump run: | npm version ${{ steps.ai.outputs.version_bump }} --no-git-tag-version @@ -147,6 +85,7 @@ jobs: echo "new_version=$NEW_VERSION" >> "$GITHUB_OUTPUT" - name: Update changelog + if: github.event_name == 'workflow_dispatch' run: | NEW_VERSION="${{ steps.bump.outputs.new_version }}" @@ -172,6 +111,7 @@ jobs: fi - name: Create release branch and PR + if: github.event_name == 'workflow_dispatch' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c2b810b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## 0.13.4 → 0.13.5 (2026-02-27) + +- Internal maintenance release (no user-facing changes) diff --git a/package-lock.json b/package-lock.json index 93b358f..1494667 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@lightsparkdev/origin", - "version": "0.13.4", + "version": "0.13.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@lightsparkdev/origin", - "version": "0.13.4", + "version": "0.13.5", "license": "Apache-2.0", "dependencies": { "@base-ui/react": "^1.1.0", diff --git a/package.json b/package.json index 283ba2f..b43c82c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightsparkdev/origin", - "version": "0.13.4", + "version": "0.13.5", "private": false, "publishConfig": { "access": "public"