shakapacker 9.6.1 → 9.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab1c412375101856684b59c852ff2fefc594d51ec5fc2ce152e3114db96a88a2
4
- data.tar.gz: c9755e1c089d5c93277ae38c7764518d03807496cbdb8f9e9c616e6a4ae2e9f6
3
+ metadata.gz: 518eb8673a021c5e18507ebc14ba593128288ddb9f735390581bd998d1f33715
4
+ data.tar.gz: 3c2c532321d8c79e4374fbbe0854319d6ebd89df33557e21a4ce6e4ec322cc7c
5
5
  SHA512:
6
- metadata.gz: 713f0fb0b2252407071faedfe5d88769f193130f43a8876d222da61b730291a067610b60be06954f3194889a6354b4f3184da0cbb6ee6670b355b61017332d12
7
- data.tar.gz: 7daa5bc48e9c82d394716acc4e4cb8bc2611d83d03f2d0b3ccff385e7255c883767b9daf23c9437dcf8ffc1ec8778bbcfe2be6d0fac89762ac043af05ad68178
6
+ metadata.gz: 26414fbf14fe2d835262ee5a6d2b46a9e50c76eee0391a0d859b60ee60f8899a87fa0e8c9d39aa651a8eadecb37639cd9684e775dd2004a55f83bb1f8aa4647e
7
+ data.tar.gz: 13f248afecdaa3ef579a20f7abf7ed592db788c2e5aa6b119a4940429b1e3c6f0bae263b985abe78e6ed0217116c6eeb9483c252f3a38bddd33e1c6e4533cc2c
@@ -1,8 +1,8 @@
1
1
  ---
2
- description: Fetch GitHub PR review comments and create todos to address them
2
+ description: Fetch GitHub PR review comments, triage them, create todos for must-fix items, reply to comments, and resolve addressed threads
3
3
  ---
4
4
 
5
- Fetch review comments from a GitHub PR in this repository and create a todo list to address each comment.
5
+ Fetch review comments from a GitHub PR in this repository, triage them, and create a todo list only for items worth addressing.
6
6
 
7
7
  # Instructions
8
8
 
@@ -54,7 +54,10 @@ gh api repos/${REPO}/pulls/{PR_NUMBER}/comments | jq '[.[] | {id: .id, path: .pa
54
54
  **Filtering comments:**
55
55
 
56
56
  - Skip comments where `in_reply_to_id` is set (these are replies, not top-level comments)
57
- - Skip bot-generated comments (check if `user` ends with `[bot]`)
57
+ - Do not skip bot-generated comments by default. Many actionable review comments in this repository come from bots.
58
+ - Deduplicate repeated bot comments and skip bot status posts, summaries, and acknowledgments that do not require a code or documentation change
59
+ - Treat as actionable by default only: correctness bugs, regressions, missing tests, and clear inconsistencies with adjacent code
60
+ - Treat as non-actionable by default: style nits, speculative suggestions, changelog wording, duplicate bot comments, and "could consider" feedback unless the user explicitly asks for polish work
58
61
  - Focus on actionable feedback, not acknowledgments or thank-you messages
59
62
 
60
63
  **Error handling:**
@@ -63,27 +66,49 @@ gh api repos/${REPO}/pulls/{PR_NUMBER}/comments | jq '[.[] | {id: .id, path: .pa
63
66
  - If the API returns 403, check authentication with `gh auth status`
64
67
  - If the response is empty, inform the user no review comments were found
65
68
 
66
- ## Step 4: Create Todo List
69
+ ## Step 4: Triage Comments
67
70
 
68
- Parse the response and create a todo list with TodoWrite containing:
71
+ Before creating any todos, classify every review comment into one of three categories:
69
72
 
70
- - One todo per actionable review comment/suggestion
73
+ - `MUST-FIX`: correctness bugs, regressions, security issues, missing tests that could hide a real bug, and clear inconsistencies with adjacent code that would likely block merge
74
+ - `DISCUSS`: reasonable suggestions that expand scope, architectural opinions that are not clearly right or wrong, and comments where the reviewer claim may be correct but needs a user decision
75
+ - `SKIPPED`: style preferences, documentation nits, comment requests, test-shape preferences, speculative suggestions, changelog wording, duplicate comments, status posts, summaries, and factually incorrect suggestions
76
+
77
+ Triage rules:
78
+
79
+ - Deduplicate overlapping comments before classifying them. Keep one representative item for the underlying issue.
80
+ - Verify factual claims locally before classifying a comment as `MUST-FIX`.
81
+ - If a claim appears wrong, classify it as `SKIPPED` and note briefly why.
82
+ - Preserve the original review comment ID and thread ID when available so the command can reply to the correct place and resolve the correct thread later.
83
+
84
+ ## Step 5: Create Todo List
85
+
86
+ Create a todo list with TodoWrite containing **only the `MUST-FIX` items**:
87
+
88
+ - One todo per must-fix comment or deduplicated issue
71
89
  - For file-specific comments: `"{file}:{line} - {comment_summary} (@{username})"` (content)
72
- - For general comments: Parse the comment body and extract actionable items
90
+ - For general comments: Parse the comment body and extract the must-fix action
73
91
  - Format activeForm: `"Addressing {brief description}"`
74
92
  - All todos should start with status: `"pending"`
75
93
 
76
- ## Step 5: Present to User
94
+ ## Step 6: Present Triage to User
77
95
 
78
- Present the todos to the user - **DO NOT automatically start addressing them**:
96
+ Present the triage to the user - **DO NOT automatically start addressing items**:
79
97
 
80
- - Show a summary of how many actionable items were found
81
- - List the todos clearly
82
- - Wait for the user to tell you which ones to address
98
+ - `MUST-FIX ({count})`: list the todos created
99
+ - `DISCUSS ({count})`: list items needing user choice, with a short reason
100
+ - `SKIPPED ({count})`: list skipped comments with a short reason, including duplicates and factually incorrect suggestions
101
+ - Wait for the user to tell you which items to address
102
+ - Always offer an explicit optional follow-up to post rationale replies on selected `SKIPPED` or declined `DISCUSS` items
103
+ - Never post those rationale replies unless the user explicitly selects which items to reply to
104
+ - Ask two things when relevant:
105
+ - Which items to address in code/tests/docs
106
+ - Which skipped/declined items (if any) should receive a rationale reply
83
107
 
84
- ## Step 6: Address Items and Reply
108
+ ## Step 7: Address Items, Reply, and Resolve
85
109
 
86
- When addressing items, after completing each todo item, reply to the original review comment explaining how it was addressed.
110
+ When addressing items, after completing each selected todo item, reply to the original review comment explaining how it was addressed.
111
+ If the user selects skipped/declined items for rationale replies, post those replies too.
87
112
 
88
113
  **For issue comments (general PR comments):**
89
114
 
@@ -111,6 +136,22 @@ The response should briefly explain:
111
136
  - Which commit(s) contain the fix
112
137
  - Any relevant details or decisions made
113
138
 
139
+ After posting the reply, resolve the review thread when all of the following are true:
140
+
141
+ - The comment belongs to a review thread and you have the thread ID
142
+ - The concern was actually addressed in code, tests, or documentation, or it was explicitly declined with a clear explanation approved by the user
143
+ - The thread is not already resolved
144
+
145
+ Use GitHub GraphQL to resolve the thread:
146
+
147
+ ```bash
148
+ gh api graphql -f query='mutation($threadId:ID!) { resolveReviewThread(input:{threadId:$threadId}) { thread { id isResolved } } }' -f threadId="<THREAD_ID>"
149
+ ```
150
+
151
+ Do not resolve a thread if the fix is still pending, if you are unsure whether the reviewer concern is satisfied, or if the user asked to leave the thread open.
152
+
153
+ If the user explicitly asks to close out a `DISCUSS` or `SKIPPED` item, reply with the rationale and resolve the thread only when the conversation is actually complete.
154
+
114
155
  # Example Usage
115
156
 
116
157
  ```text
@@ -122,16 +163,25 @@ The response should briefly explain:
122
163
 
123
164
  # Example Output
124
165
 
125
- After fetching comments, present them like this:
166
+ After fetching and triaging comments, present them like this:
126
167
 
127
168
  ```text
128
- Found 3 actionable review comments:
169
+ Found 5 review comments. Triage:
170
+
171
+ MUST-FIX (1):
172
+ 1. ⬜ src/helper.rb:45 - Missing nil guard causes a crash on empty input (@reviewer1)
173
+
174
+ DISCUSS (1):
175
+ 2. src/config.rb:12 - Extract this to a shared config constant (@reviewer1)
176
+ Reason: reasonable suggestion, but it expands scope
129
177
 
130
- 1. ⬜ src/helper.rb:45 - Add error handling for nil case (@reviewer1)
131
- 2. src/config.rb:12 - Consider using constant instead of magic number (@reviewer1)
132
- 3. General comment - Update documentation to reflect API changes (@reviewer2)
178
+ SKIPPED (3):
179
+ 3. src/helper.rb:50 - "Consider adding a comment" (@claude[bot]) - documentation nit
180
+ 4. src/helper.rb:45 - Same nil guard issue (@greptile-apps[bot]) - duplicate of #1
181
+ 5. spec/helper_spec.rb:20 - "Consolidate assertions" (@claude[bot]) - test style preference
133
182
 
134
- Which items would you like me to address? (e.g., "all", "1,2", or "1")
183
+ Which items would you like me to address? (e.g., "1", "1,2", or "all must-fix")
184
+ Optional: I can also post rationale replies for skipped/declined items (e.g., "reply 3,5" or "reply all skipped").
135
185
  ```
136
186
 
137
187
  # Important Notes
@@ -144,10 +194,13 @@ Which items would you like me to address? (e.g., "all", "1,2", or "1")
144
194
  - **NEVER automatically address all review comments** - always wait for user direction
145
195
  - When given a specific review URL, no need to ask for more information
146
196
  - **ALWAYS reply to comments after addressing them** to close the feedback loop
197
+ - After triage, always offer to post rationale replies for selected `SKIPPED`/declined items, but only post them with explicit user approval
198
+ - Resolve the review thread after replying when the concern is actually addressed and a thread ID is available
199
+ - Default to real issues only. Do not spend a review cycle on optional polish unless the user explicitly asks for it
200
+ - Triage comments before creating todos. Only `MUST-FIX` items should become todos by default
147
201
  - For large review comments (like detailed code reviews), parse and extract the actionable items into separate todos
148
202
 
149
203
  # Known Limitations
150
204
 
151
205
  - Rate limiting: GitHub API has rate limits; if you hit them, wait a few minutes
152
206
  - Private repos: Requires appropriate `gh` authentication scope
153
- - Large PRs: PRs with many comments may require pagination (not currently handled)
@@ -7,9 +7,10 @@ You are helping to add an entry to the CHANGELOG.md file for the Shakapacker pro
7
7
  This command accepts an optional argument: `$ARGUMENTS`
8
8
 
9
9
  - **No argument** (`/update-changelog`): Add entries to `[Unreleased]` without stamping a version header. Use this during development.
10
- - **`release`** (`/update-changelog release`): Add entries and stamp a version header. Auto-compute the next version based on changes (breaking → major, added features → minor, fixes → patch). Then `rake create_release` (with no args) will pick up this version automatically.
10
+ - **`release`** (`/update-changelog release`): Add entries and stamp a version header. Auto-compute the next version based on changes (breaking → major, added features → minor, fixes → patch). Then `rake release` (with no args) will pick up this version automatically.
11
11
  - **`rc`** (`/update-changelog rc`): Same as `release`, but stamps an RC prerelease version (e.g., `v9.7.0-rc.0`). Auto-increments the RC index if prior RCs exist for the same base version.
12
12
  - **`beta`** (`/update-changelog beta`): Same as `rc`, but stamps a beta prerelease version (e.g., `v9.7.0-beta.0`).
13
+ - **Explicit version** (`/update-changelog 9.7.0-rc.10` or `/update-changelog v9.7.0-rc.10`): Add entries and stamp the exact version provided. Skips auto-computation — use this when you already know the target version. The version string must use npm semver format with optional `-rc.N` or `-beta.N` suffix (e.g., `9.7.0-rc.10`, `9.7.0`). A `v` prefix is optional and will be added automatically if missing. If passed in RubyGems dot format (e.g., `9.7.0.rc.10` or `9.7.0.beta.2`), convert to npm semver dash format (`v9.7.0-rc.10` or `v9.7.0-beta.2`) for the changelog header.
13
14
 
14
15
  ## When to Use This
15
16
 
@@ -25,7 +26,7 @@ This command serves three use cases at different points in the release lifecycle
25
26
  - Run `/update-changelog release` (or `rc` or `beta`) to add entries AND stamp the version header
26
27
  - The version is auto-computed from changelog content (see "Auto-Computing the Next Version" below)
27
28
  - Commit and push CHANGELOG.md
28
- - Then run `rake create_release` (no args needed — it reads the version from CHANGELOG.md)
29
+ - Then run `rake release` (no args needed — it reads the version from CHANGELOG.md)
29
30
  - The release task automatically creates a GitHub release from the changelog section
30
31
 
31
32
  **After a release you forgot to update the changelog for** — Catch-up mode:
@@ -35,7 +36,7 @@ This command serves three use cases at different points in the release lifecycle
35
36
 
36
37
  ### Why changelog comes BEFORE the release
37
38
 
38
- - `create_release` automatically creates a GitHub release if a changelog section exists — no separate `sync_github_release` step needed
39
+ - `release` automatically creates a GitHub release if a changelog section exists — no separate `sync_github_release` step needed
39
40
  - The release task warns if no changelog section is found for the target version
40
41
  - A premature version header (if release fails) is harmless — you'll release eventually
41
42
  - A missing changelog after release means GitHub release must be created manually
@@ -57,12 +58,12 @@ When stamping a version header (`release`, `rc`, or `beta`), compute the next ve
57
58
 
58
59
  3. **Compute the version**:
59
60
  - For `release`: Apply the bump to the latest stable tag (e.g., `9.5.0` + minor → `9.6.0`)
60
- - For `rc`: Apply the bump, then find the next RC index (e.g., if `v9.6.0-rc.0` tag exists → `v9.6.0-rc.1`)
61
+ - For `rc`: Apply the bump, then find the next RC index based **only on git tags** (e.g., if `v9.6.0-rc.0` tag exists → `v9.6.0-rc.1`). **Do NOT use changelog headers** to determine the next index — a version header in the changelog is a draft that may not have been released yet. Only git tags represent shipped versions.
61
62
  - For `beta`: Same as RC but with beta suffix
62
63
 
63
64
  4. **Verify**: Check that the computed version is newer than ALL existing tags (stable and prerelease). If not, ask the user what to do.
64
65
 
65
- 5. **Show the computed version to the user** and ask for confirmation before stamping the header.
66
+ 5. **Show the computed version to the user and ask for confirmation** before stamping the header. If the bump type is ambiguous (e.g., changes could reasonably be classified as patch vs minor, or the changelog headings don't clearly signal the bump level), explain your reasoning for the suggested bump and ask the user to confirm or override before proceeding.
66
67
 
67
68
  ## Critical Requirements
68
69
 
@@ -168,6 +169,8 @@ This will:
168
169
 
169
170
  After adding an entry to the `## [Unreleased]` section, ensure the version diff links at the bottom of the file are correct.
170
171
 
172
+ **IMPORTANT**: Compare links at the bottom MUST use the `v` prefix to match git tags (e.g., `.../compare/v9.2.0...v9.3.0`). This is consistent with Shakapacker's changelog headers which also include the `v` prefix (e.g., `## [v9.3.0]`).
173
+
171
174
  The format at the bottom should be:
172
175
 
173
176
  ```markdown
@@ -186,59 +189,89 @@ When a new version is released:
186
189
 
187
190
  ### For Regular Changelog Updates
188
191
 
189
- 1. **ALWAYS fetch latest changes first**:
190
- - **CRITICAL**: Run `git fetch origin main --tags` to ensure you have the latest commits AND tags
191
- - The workspace may be behind origin/main, causing you to miss recently merged PRs
192
- - After fetching, use `origin/main` for all comparisons, NOT local `main` branch
193
-
194
- 2. **Determine the correct version tag to compare against**:
195
- - List all version tags sorted by semver: `git tag -l 'v*' --sort=-v:refname | head -10`
196
- - This correctly sorts RC/beta tags (e.g., `v9.6.0-rc.1` sorts after `v9.6.0-rc.0` and before `v9.6.0`)
197
- - The latest tag may be a stable release, RC, or beta — handle all cases
198
- - Compare origin/main branch date to the tag date using: `git log -1 --format="%ai" origin/main` and `git log -1 --format="%ai" <tag>`
199
- - If the tag is NEWER than origin/main, the branch needs updating to include the tag's commits
200
- - **CRITICAL**: Always use `git log TAG..BRANCH` to find commits in the branch but not the tag, AND `git log BRANCH..TAG` to check if the tag is ahead
201
-
202
- 3. **Check commits and version boundaries**:
203
- - **IMPORTANT**: Use `origin/main` in all commands below, not local `main`
204
- - Run `git log --oneline LAST_TAG..origin/main` to see commits since the last release
205
- - Also check `git log --oneline origin/main..LAST_TAG` to see if the tag is ahead of origin/main
206
- - If the tag is ahead, entries in "Unreleased" section may actually belong to that tagged version
207
- - **Extract ALL PR numbers** from commit messages using grep: `git log --oneline LAST_TAG..origin/main | grep -oE "#[0-9]+" | sort -u`
208
- - For each PR number found, check if it's already in CHANGELOG.md using: `grep "PR #XXX" CHANGELOG.md`
209
- - Identify which commits contain user-visible changes (look for keywords like "Fix", "Add", "Feature", "Bug", etc.)
210
- - Extract author information from commit messages
211
- - **Never ask the user for PR details** - get them from the git history or use WebFetch on the PR URL
212
-
213
- 4. **Validate** that changes are user-visible (per the criteria above). If not user-visible, skip those commits.
214
-
215
- 5. **Read the current CHANGELOG.md** to understand the existing structure and formatting.
216
-
217
- 6. **Determine where entries should go**:
218
- - If the latest version tag is NEWER than origin/main branch, move entries from "Unreleased" to that version section
219
- - If origin/main is ahead of the latest tag, add new entries to "Unreleased"
220
- - Always verify the version date in CHANGELOG.md matches the actual tag date
221
-
222
- 7. **Add or move entries** to the appropriate section under appropriate category headings.
223
- - **CRITICAL**: When moving entries from "Unreleased" to a version section, merge them with existing entries under the same category heading
224
- - **NEVER create duplicate section headings** (e.g., don't create two "### Fixed" sections)
225
- - If the version section already has a category heading (e.g., "### Fixed"), add the moved entries to that existing section
226
- - Maintain the category order as defined above
227
-
228
- 8. **Verify formatting**:
192
+ #### Step 1: Fetch and read current state
193
+
194
+ - **CRITICAL**: Run `git fetch origin main --tags` to ensure you have the latest commits AND tags
195
+ - The workspace may be behind origin/main, causing you to miss recently merged PRs
196
+ - After fetching, use `origin/main` for all comparisons, NOT local `main` branch
197
+ - Read the current CHANGELOG.md to understand the existing structure
198
+
199
+ #### Step 2: Reconcile tags with changelog sections (DO THIS FIRST)
200
+
201
+ **This step catches missing version sections and is the #1 source of errors when skipped.**
202
+
203
+ 1. Get the latest git tag: `git tag -l 'v*' --sort=-v:refname | head -5`
204
+ 2. Get the most recent version header in CHANGELOG.md (the first `## [vVERSION]` after `## [Unreleased]`)
205
+ 3. **Compare them.** If the latest git tag does NOT appear anywhere in the changelog version headers, there are tagged releases missing from the changelog. **Important**: Don't just compare against the _top_ changelog header — a version header may exist _above_ the latest tag if it was stamped as a draft before tagging. Check whether the tag's version appears in _any_ `## [vX.Y.Z]` header. For example:
206
+ - Latest tag: `v9.6.0-rc.4`, and no `## [v9.6.0-rc.4]` header exists anywhere in CHANGELOG.md
207
+ - **Result: `v9.6.0-rc.4` is missing and needs its own section**
208
+ - But if `## [v9.7.0-rc.0]` is the top header (a draft, not yet tagged) and `## [v9.6.0-rc.4]` exists below it, then nothing is missing — the top header is simply a pre-release draft
209
+
210
+ 4. For EACH missing tagged version (there may be multiple):
211
+ a. Find commits in that tag vs the previous tag: `git log --oneline PREV_TAG..MISSING_TAG`
212
+ b. Extract PR numbers and fetch details for user-visible changes
213
+ c. Check which entries currently in `## [Unreleased]` actually belong to this tagged version (compare PR numbers against the commit list)
214
+ d. **Create a new version section** immediately before the previous version section
215
+ e. **Move** matching entries from Unreleased into the new section
216
+ f. **Add** any new entries for PRs in that tag that aren't in the changelog at all
217
+ g. **Update version diff links** at the bottom of the file
218
+
219
+ 5. Get the tag date with: `git log -1 --format="%Y-%m-%d" TAG_NAME`
220
+
221
+ #### Step 3: Add new entries for post-tag commits
222
+
223
+ 1. Run `git log --oneline LATEST_TAG..origin/main` to find commits after the latest tag (LATEST_TAG is the most recent git tag, i.e., the same one identified in Step 2)
224
+ 2. **Extract ALL PR numbers** from commit messages: `git log --oneline LATEST_TAG..origin/main | grep -oE "#[0-9]+" | sort -u`
225
+ 3. If Step 2 found no missing tagged versions, verify no tag is ahead of main: `git log --oneline origin/main..LATEST_TAG` should be empty. If not, entries in "Unreleased" may belong to that tagged version Step 2 should have caught this, so re-check.
226
+ 4. For each PR number, check if it's already in CHANGELOG.md: `grep "PR #XXX" CHANGELOG.md`
227
+ 5. For PRs not yet in the changelog:
228
+ - Get PR details: `gh pr view NUMBER --json title,body,author --repo shakacode/shakapacker`
229
+ - **Never ask the user for PR details** — get them from git history or the GitHub API
230
+ - Validate that the change is user-visible (per the criteria above). Skip CI, lint, refactoring, test-only changes.
231
+ - Add the entry to `## [Unreleased]` under the appropriate category heading
232
+
233
+ #### Step 4: Stamp version header (only when a version mode or explicit version is given)
234
+
235
+ If the user passed `release`, `rc`, `beta`, or an explicit version string as an argument:
236
+
237
+ 1. Auto-compute the next version (see "Auto-Computing the Next Version" above), or use the explicit version provided
238
+ 2. Insert the version header immediately after `## [Unreleased]`
239
+ 3. For `rc`/`beta` or an explicit prerelease version (e.g., `9.7.0-rc.10`): collapse prior prerelease sections of the same base version into the new section
240
+ 4. Update version diff links at the bottom of the file
241
+ 5. **Verify** the computed version looks correct
242
+
243
+ If no argument was passed, skip this step — entries stay in `## [Unreleased]`.
244
+
245
+ #### Step 5: Verify and finalize
246
+
247
+ 1. **Verify formatting**:
229
248
  - Bold description with period
230
- - Proper PR link
249
+ - Proper PR link (with `#` prefix for Shakapacker)
231
250
  - Proper author link
232
251
  - Consistent with existing entries
233
252
  - File ends with a newline character
234
-
235
- 9. **Run linting** after making changes:
253
+ - **No duplicate section headings** (e.g., don't create two `### Fixed` sections — merge entries into the existing heading)
254
+ 2. **Verify version sections are in order** (Unreleased newest tag → older tags)
255
+ 3. **Verify version diff links** at the bottom of the file are correct (compare links MUST use the `v` prefix to match git tags)
256
+ 4. **Run linting** after making changes:
236
257
 
237
258
  ```bash
238
259
  yarn lint
239
260
  ```
240
261
 
241
- 10. **Show the user** the added or moved entries and explain what was done.
262
+ 5. **Show the user** a summary of what was done:
263
+ - Which version sections were created
264
+ - Which entries were moved from Unreleased
265
+ - Which new entries were added
266
+ - Which PRs were skipped (and why)
267
+ 6. If in `release`/`rc`/`beta` mode or explicit-version mode, **automatically commit, push, and open a PR**:
268
+ - Verify the working tree only has `CHANGELOG.md` changes; if there are other uncommitted changes, warn the user and stop
269
+ - Verify the current branch is `main` (`git branch --show-current`); if not, warn the user and stop
270
+ - Create a feature branch (e.g., `changelog-v9.6.0-rc.1`)
271
+ - Stage only `CHANGELOG.md` (`git add CHANGELOG.md`) and commit with message `Update CHANGELOG.md for vX.Y.Z` (using the stamped version)
272
+ - Push and open a PR with the changelog diff as the body
273
+ - If the push or PR creation fails, the CHANGELOG is already stamped locally — fix the issue and retry manually
274
+ - Remind the user to run `bundle exec rake release` (no args) after merge to publish and auto-create the GitHub release
242
275
 
243
276
  ### For Prerelease Versions (RC and Beta)
244
277
 
@@ -259,10 +292,14 @@ When the user passes `rc` or `beta` as an argument (or when creating a prereleas
259
292
  4. **Always collapse prior prereleases into the current prerelease** (this is the default behavior):
260
293
  - Combine all prior prerelease changelog entries into the new prerelease version section
261
294
  - Remove previous prerelease version sections (e.g., remove `## [v9.6.0-rc.0]` when creating `## [v9.6.0-rc.1]`)
295
+ - When collapsing, **consolidate duplicate category headings** — if both the Unreleased section and a prior prerelease section have `### Fixed`, merge all entries under a single `### Fixed` heading
296
+ - **Remove orphaned version diff links** at the bottom of the file for collapsed prerelease sections
262
297
  - Add any new user-visible changes from commits since the last prerelease
263
298
  - Update version diff links to point from the last stable version to the new prerelease
264
299
  - This keeps the changelog clean with a single prerelease section that accumulates all changes since the last stable release
265
300
 
301
+ **Note**: The new version header must be inserted **immediately after `## [Unreleased]`** (see Step 4). This ensures correct ordering of version headers.
302
+
266
303
  ### For Prerelease to Stable Version Release
267
304
 
268
305
  When releasing from prerelease to a stable version (e.g., v9.6.0-rc.1 → v9.6.0):
@@ -16,7 +16,7 @@ jobs:
16
16
 
17
17
  steps:
18
18
  - name: Checkout repository
19
- uses: actions/checkout@v6
19
+ uses: actions/checkout@v4
20
20
  with:
21
21
  fetch-depth: 1
22
22
 
data/CHANGELOG.md CHANGED
@@ -9,6 +9,17 @@
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [v9.7.0] - March 15, 2026
13
+
14
+ ### Added
15
+
16
+ - **Added rspack v2 support**. [PR #975](https://github.com/shakacode/shakapacker/pull/975) by [justin808](https://github.com/justin808). Peer dependencies now accept both rspack v1 and v2 (`^1.0.0 || ^2.0.0-0`). No source code changes were needed — all existing APIs work identically in v2. Note that rspack v2 requires Node.js 20.19.0+.
17
+
18
+ ### Fixed
19
+
20
+ - **Fixed config exporter path traversal and annotation format validation**. [PR #914](https://github.com/shakacode/shakapacker/pull/914) by [justin808](https://github.com/justin808). Added `safeResolvePath` security check to prevent path traversal in export save paths, and enforced YAML format when using annotations with build exports.
21
+ - **Fixed `webpack-subresource-integrity` v5 named export handling**. [PR #978](https://github.com/shakacode/shakapacker/pull/978) by [justin808](https://github.com/justin808). Supports both the default export (older versions) and the named `SubresourceIntegrityPlugin` export (v5.1+), preventing runtime breakage when upgrading the plugin. Fixes [#972](https://github.com/shakacode/shakapacker/issues/972).
22
+
12
23
  ## [v9.6.1] - March 8, 2026
13
24
 
14
25
  ### Fixed
@@ -876,7 +887,8 @@ Note: [Rubygem is 6.3.0.pre.rc.1](https://rubygems.org/gems/shakapacker/versions
876
887
 
877
888
  See [CHANGELOG.md in rails/webpacker (up to v5.4.3)](https://github.com/rails/webpacker/blob/master/CHANGELOG.md)
878
889
 
879
- [Unreleased]: https://github.com/shakacode/shakapacker/compare/v9.6.1...main
890
+ [Unreleased]: https://github.com/shakacode/shakapacker/compare/v9.7.0...main
891
+ [v9.7.0]: https://github.com/shakacode/shakapacker/compare/v9.6.1...v9.7.0
880
892
  [v9.6.1]: https://github.com/shakacode/shakapacker/compare/v9.6.0...v9.6.1
881
893
  [v9.6.0]: https://github.com/shakacode/shakapacker/compare/v9.5.0...v9.6.0
882
894
  [v9.5.0]: https://github.com/shakacode/shakapacker/compare/v9.4.0...v9.5.0
data/Rakefile CHANGED
@@ -2,6 +2,11 @@
2
2
  require "bundler/gem_tasks"
3
3
  require "pathname"
4
4
 
5
+ # Remove Bundler's default `release` task — it bypasses the custom release flow
6
+ # (CHANGELOG version detection, npm publish, GitHub release sync, etc.).
7
+ # The custom `release` task in rakelib/release.rake replaces it.
8
+ Rake::Task[:release].clear
9
+
5
10
  desc "Run all specs"
6
11
  task test: ["run_spec:all_specs"]
7
12
 
@@ -15,33 +15,33 @@ const rspack = require("shakapacker/rspack")
15
15
 
16
16
  ## Webpack Exports (`shakapacker`)
17
17
 
18
- | Export | Type | Description |
19
- |--------|------|-------------|
20
- | `config` | object | Parsed `config/shakapacker.yml` plus computed fields |
21
- | `devServer` | object | Dev server configuration |
22
- | `generateWebpackConfig(extraConfig?)` | function | Generates final webpack config and merges optional overrides |
23
- | `baseConfig` | object | Base config object from `package/environments/base` |
24
- | `env` | object | Environment metadata (`railsEnv`, `nodeEnv`, booleans) |
25
- | `rules` | array | Loader rules for current bundler |
26
- | `moduleExists(name)` | function | Returns whether module can be resolved |
27
- | `canProcess(rule, fn)` | function | Runs callback only if loader dependency is available |
28
- | `inliningCss` | boolean | Whether CSS should be inlined in current dev-server mode |
29
- | `merge`, `mergeWithCustomize`, `mergeWithRules`, `unique` | functions | Re-exported from `webpack-merge` |
18
+ | Export | Type | Description |
19
+ | --------------------------------------------------------- | --------- | ------------------------------------------------------------ |
20
+ | `config` | object | Parsed `config/shakapacker.yml` plus computed fields |
21
+ | `devServer` | object | Dev server configuration |
22
+ | `generateWebpackConfig(extraConfig?)` | function | Generates final webpack config and merges optional overrides |
23
+ | `baseConfig` | object | Base config object from `package/environments/base` |
24
+ | `env` | object | Environment metadata (`railsEnv`, `nodeEnv`, booleans) |
25
+ | `rules` | array | Loader rules for current bundler |
26
+ | `moduleExists(name)` | function | Returns whether module can be resolved |
27
+ | `canProcess(rule, fn)` | function | Runs callback only if loader dependency is available |
28
+ | `inliningCss` | boolean | Whether CSS should be inlined in current dev-server mode |
29
+ | `merge`, `mergeWithCustomize`, `mergeWithRules`, `unique` | functions | Re-exported from `webpack-merge` |
30
30
 
31
31
  ## Rspack Exports (`shakapacker/rspack`)
32
32
 
33
- | Export | Type | Description |
34
- |--------|------|-------------|
35
- | `config` | object | Parsed `config/shakapacker.yml` plus computed fields |
36
- | `devServer` | object | Dev server configuration |
37
- | `generateRspackConfig(extraConfig?)` | function | Generates final rspack config and merges optional overrides |
38
- | `baseConfig` | object | Base config object |
39
- | `env` | object | Environment metadata (`railsEnv`, `nodeEnv`, booleans) |
40
- | `rules` | array | Rspack loader rules |
41
- | `moduleExists(name)` | function | Returns whether module can be resolved |
42
- | `canProcess(rule, fn)` | function | Runs callback only if loader dependency is available |
43
- | `inliningCss` | boolean | Whether CSS should be inlined in current dev-server mode |
44
- | `merge`, `mergeWithCustomize`, `mergeWithRules`, `unique` | functions | Re-exported from `webpack-merge` |
33
+ | Export | Type | Description |
34
+ | --------------------------------------------------------- | --------- | ----------------------------------------------------------- |
35
+ | `config` | object | Parsed `config/shakapacker.yml` plus computed fields |
36
+ | `devServer` | object | Dev server configuration |
37
+ | `generateRspackConfig(extraConfig?)` | function | Generates final rspack config and merges optional overrides |
38
+ | `baseConfig` | object | Base config object |
39
+ | `env` | object | Environment metadata (`railsEnv`, `nodeEnv`, booleans) |
40
+ | `rules` | array | Rspack loader rules |
41
+ | `moduleExists(name)` | function | Returns whether module can be resolved |
42
+ | `canProcess(rule, fn)` | function | Runs callback only if loader dependency is available |
43
+ | `inliningCss` | boolean | Whether CSS should be inlined in current dev-server mode |
44
+ | `merge`, `mergeWithCustomize`, `mergeWithRules`, `unique` | functions | Re-exported from `webpack-merge` |
45
45
 
46
46
  ## `config` Object
47
47
 
@@ -68,4 +68,3 @@ Installer defaults include support for:
68
68
  Dependency presets used by the installer are defined in:
69
69
 
70
70
  - [`lib/install/package.json`](../lib/install/package.json)
71
-
data/docs/releasing.md CHANGED
@@ -47,26 +47,29 @@ The simplest way to release is with no arguments — the task reads the version
47
47
 
48
48
  ```bash
49
49
  # Recommended: reads version from CHANGELOG.md (requires step 1)
50
- bundle exec rake create_release
50
+ bundle exec rake release
51
51
 
52
52
  # For a specific version (overrides CHANGELOG.md detection)
53
- bundle exec rake "create_release[9.1.0]"
53
+ bundle exec rake "release[9.1.0]"
54
54
 
55
55
  # For a beta release (note: use period, not dash)
56
- bundle exec rake "create_release[9.2.0.beta.1]" # Creates npm package 9.2.0-beta.1
56
+ bundle exec rake "release[9.2.0.beta.1]" # Creates npm package 9.2.0-beta.1
57
57
 
58
58
  # For a release candidate
59
- bundle exec rake "create_release[9.6.0.rc.0]"
59
+ bundle exec rake "release[9.6.0.rc.0]"
60
60
 
61
61
  # Dry run to test without publishing
62
- bundle exec rake "create_release[9.1.0,true]"
62
+ bundle exec rake "release[9.1.0,true]"
63
+
64
+ # Skip interactive confirmations (for scripted maintainer runs)
65
+ AUTO_CONFIRM=true bundle exec rake release
63
66
 
64
67
  # Override version policy checks (monotonic + changelog/bump consistency)
65
- RELEASE_VERSION_POLICY_OVERRIDE=true bundle exec rake "create_release[9.1.0]"
66
- bundle exec rake "create_release[9.1.0,false,true]"
68
+ RELEASE_VERSION_POLICY_OVERRIDE=true bundle exec rake "release[9.1.0]"
69
+ bundle exec rake "release[9.1.0,false,true]"
67
70
  ```
68
71
 
69
- When called with no arguments, `create_release`:
72
+ When called with no arguments, `release`:
70
73
 
71
74
  1. Reads the first versioned header from CHANGELOG.md (e.g., `## [v9.6.0]`)
72
75
  2. Compares it to the current gem version
@@ -74,8 +77,9 @@ When called with no arguments, `create_release`:
74
77
  4. If no new version is found, falls back to a patch bump
75
78
 
76
79
  Dry runs use a temporary git worktree so version bumps and installs do not modify your current checkout.
80
+ Dry runs now also print explicit "skipping confirmation" messages and the would-run GitHub release command.
77
81
 
78
- `create_release` validates release-version policy before publishing:
82
+ `release` validates release-version policy before publishing:
79
83
 
80
84
  - Target version must be greater than the latest tagged release.
81
85
  - If the versioned target changelog section exists (`## [vX.Y.Z...]`; not `UNRELEASED`), it maps to expected bump type:
@@ -87,11 +91,11 @@ Dry runs use a temporary git worktree so version bumps and installs do not modif
87
91
  Use override only when needed:
88
92
 
89
93
  - `RELEASE_VERSION_POLICY_OVERRIDE=true`
90
- - Or task arg override (`create_release[..., ..., true]`)
94
+ - Or task arg override (`release[..., ..., true]`)
91
95
 
92
96
  ### 3. What the Release Task Does
93
97
 
94
- The `create_release` task automatically:
98
+ The `release` task automatically:
95
99
 
96
100
  1. **Validates release prerequisites**:
97
101
  - Verifies npm authentication
@@ -134,24 +138,26 @@ The task automatically converts Ruby gem format to npm semver format:
134
138
 
135
139
  ```bash
136
140
  # Regular release
137
- bundle exec rake "create_release[9.1.0]" # Gem: 9.1.0, npm: 9.1.0
141
+ bundle exec rake "release[9.1.0]" # Gem: 9.1.0, npm: 9.1.0
138
142
 
139
143
  # Beta release
140
- bundle exec rake "create_release[9.2.0.beta.1]" # Gem: 9.2.0.beta.1, npm: 9.2.0-beta.1
144
+ bundle exec rake "release[9.2.0.beta.1]" # Gem: 9.2.0.beta.1, npm: 9.2.0-beta.1
141
145
 
142
146
  # Release candidate
143
- bundle exec rake "create_release[10.0.0.rc.1]" # Gem: 10.0.0.rc.1, npm: 10.0.0-rc.1
147
+ bundle exec rake "release[10.0.0.rc.1]" # Gem: 10.0.0.rc.1, npm: 10.0.0-rc.1
144
148
 
145
- # Prerelease: use /update-changelog rc first, then create_release reads it
146
- bundle exec rake create_release # reads v10.0.0-rc.0 from CHANGELOG.md
149
+ # Prerelease: use /update-changelog rc first, then release reads it
150
+ bundle exec rake release # reads v10.0.0-rc.0 from CHANGELOG.md
147
151
  ```
148
152
 
149
153
  ### 5. During the Release
150
154
 
155
+ If you are running non-interactively, set `AUTO_CONFIRM=true` to skip confirmation prompts.
156
+
151
157
  1. When prompted for **npm OTP**, enter your 2FA code from your authenticator app
152
158
  2. Accept defaults for release-it options
153
159
  3. When prompted for **RubyGems OTP**, enter your 2FA code
154
- 4. If using `create_release` with no version, confirm the version detected from CHANGELOG.md (or the computed patch version)
160
+ 4. If using `release` with no version, confirm the version detected from CHANGELOG.md (or the computed patch version)
155
161
  5. The script will automatically commit and push lockfile updates
156
162
  6. The script will automatically create a GitHub release (if CHANGELOG.md section exists)
157
163