diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index 586c4c7..72ae952 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -4,8 +4,8 @@ env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" # On every push to main: bump cli/package.json (create-page badge) and -# prepend an OpenAI-written Keep-a-Changelog section. Commits directly -# with [changelog][skip ci] so tests and this workflow do not loop. +# prepend an OpenAI-written Keep-a-Changelog section, then open a PR. +# (main is protected — direct pushes are rejected.) on: push: @@ -21,25 +21,54 @@ jobs: name: Update changelog & version runs-on: ubuntu-latest timeout-minutes: 10 - if: ${{ github.event_name == 'workflow_dispatch' || !contains(github.event.head_commit.message, '[changelog]') }} + # Skip bot merges and pushes that only touched changelog/version files. + if: > + github.event_name == 'workflow_dispatch' || + ( + !contains(github.event.head_commit.message, '[changelog]') && + !startsWith(github.event.head_commit.message, 'docs: release v') + ) permissions: contents: write + pull-requests: write steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - # Needed so the bot commit triggers deploy hooks that listen to main. - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.GH_PAT }} # Uses your custom token to bypass restrictions + + - name: Skip if push is changelog-only + id: guard + if: github.event_name == 'push' + run: | + BEFORE="${{ github.event.before }}" + AFTER="${{ github.sha }}" + if [[ -z "$BEFORE" || "$BEFORE" =~ ^0+$ ]]; then + echo "skip=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + FILES=$(git diff --name-only "$BEFORE" "$AFTER" || true) + echo "Changed files:" + echo "$FILES" + NON_CHANGELOG=$(echo "$FILES" | grep -vE '^(CHANGELOG\.md|cli/package\.json)$' || true) + if [[ -z "$NON_CHANGELOG" && -n "$FILES" ]]; then + echo "Only changelog/version files changed — skipping to avoid a loop." + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi - name: Setup Node + if: steps.guard.outputs.skip != 'true' uses: actions/setup-node@v4 with: node-version: "22" - name: Generate changelog + bump version id: release + if: steps.guard.outputs.skip != 'true' env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} OPENAI_MODEL: gpt-4o-mini @@ -47,14 +76,42 @@ jobs: AFTER_SHA: ${{ github.sha }} run: node scripts/update-changelog.mjs - - name: Commit CHANGELOG.md and cli/package.json + - name: Open changelog PR + if: steps.guard.outputs.skip != 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ steps.release.outputs.version }} run: | git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add CHANGELOG.md cli/package.json if git diff --staged --quiet; then echo "No changelog/version changes to commit." exit 0 fi - git commit -m "docs: release v${{ steps.release.outputs.version }} [changelog][skip ci]" - git push + + BRANCH="chore/changelog-v${VERSION}" + git checkout -B "$BRANCH" + git commit -m "docs: release v${VERSION} [changelog][skip ci]" + git push -u origin "$BRANCH" --force + + EXISTING=$(gh pr list --head "$BRANCH" --base main --json number --jq '.[0].number // empty') + if [[ -n "$EXISTING" ]]; then + echo "PR #$EXISTING already open for $BRANCH" + PR_URL=$(gh pr view "$EXISTING" --json url --jq .url) + else + PR_URL=$(gh pr create --base main --head "$BRANCH" \ + --title "docs: release v${VERSION} [changelog]" \ + --body "## Summary + - Bump product version to **v${VERSION}** (\`cli/package.json\` — shown on \`/create\`) + - Prepend Keep-a-Changelog entry for this push to \`main\` + + Auto-generated by the Changelog + Version workflow.") + fi + + echo "PR: $PR_URL" + + # Best-effort auto-merge (needs branch rules that allow the Actions bot). + gh pr merge "$PR_URL" --squash --auto --delete-branch || \ + echo "Auto-merge not enabled or not permitted — merge the PR manually." diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5e90730..c289be2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,7 +46,7 @@ Please follow these naming standards for your branches: ## Changelog & versioning -Product version lives in `cli/package.json` and is shown on `/create`. On every push to `main`, GitHub Actions bumps that version and prepends an entry to `CHANGELOG.md` (OpenAI-written, committed with `[changelog][skip ci]`). +Product version lives in `cli/package.json` and is shown on `/create`. On every push to `main`, GitHub Actions bumps that version, prepends an entry to `CHANGELOG.md`, and opens a PR (main is protected). Merge that PR to publish the release notes and version badge update. Do **not** hand-edit version bumps for routine merges — the workflow owns them. Large product stories still belong in `content/blog/updates/` as MDX announcements. diff --git a/README.md b/README.md index e862f7c..02e2b24 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ FlutterInit ships a built-in blog at [flutterinit.com/blogs](https://flutterinit | **Updates** | Release notes, announcements, and major product stories | | **Guides** | Deep-dive technical guides for every supported stack combination | -The living product changelog lives at [flutterinit.com/changelog](https://flutterinit.com/changelog) (source: [`CHANGELOG.md`](CHANGELOG.md)). The version badge on [/create](https://flutterinit.com/create) tracks [`cli/package.json`](cli/package.json) and is bumped automatically on every push to `main`. +The living product changelog lives at [flutterinit.com/changelog](https://flutterinit.com/changelog) (source: [`CHANGELOG.md`](CHANGELOG.md)). The version badge on [/create](https://flutterinit.com/create) tracks [`cli/package.json`](cli/package.json); every push to `main` opens a changelog PR that bumps it. Every guide shows the exact `stackConfig` it covers (Architecture · State · Backend · Navigation), a "when to choose this stack" recommendation, an interactive file tree of the generated output, and a Table of Contents sidebar.