ci: fix ERELEASEBRANCHES and allow RC version bump to dev#2188
Conversation
- Add 'main' as non-prerelease anchor for RC mode (semantic-release v25 requires >= 1 non-prerelease branch; prerelease-only config throws ERELEASEBRANCHES with empty []) - Stable mode uses 'dev' directly as release branch - Strip prerelease suffix in CMakeLists replacement so CMake gets '1.5.0' not '1.5.0-rc.1' (CMake VERSION rejects prerelease syntax) - Add git identity config step so @semantic-release/git can commit version bump back to dev for both RC and stable releases Prereq: 'main' branch must exist on remote (create from v1.4.11 tag). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe PR updates CI/release configuration: it sets a deterministic Git author in the release workflow before running version-audit tooling, and changes release branching and CMake version replacement logic to strip prerelease suffixes and treat RC releases differently. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
No actionable suggestions for changed features. |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.releaserc.js (1)
19-27:⚠️ Potential issue | 🟠 MajorAllow no-op CMake replacements after stripping RC suffix.
Line 20 strips the RC suffix, writing only the base version (e.g.,
1.5.0instead of1.5.0-rc.1). When releasing a second RC for the same base version or the final stable release after an RC, the sameVERSION x.y.zmay already exist inCMakeLists.txt. In this case,replace-in-filereportshasChanged: false/numReplacements: 0while the configured results requirehasChanged: true/numReplacements: 1. The semantic-release-replace-plugin uses exact comparison (isEqual) on line 212 of its source, so this mismatch causes the release to fail despite finding a valid match.🛠️ Proposed fix
// Strip prerelease suffix so CMake gets '1.5.0' not '1.5.0-rc.1' to: "VERSION ${nextRelease.version.split('-')[0]}", - results: [ - { - file: 'CMakeLists.txt', - hasChanged: true, - numReplacements: 1, - numMatches: 1, - }, - ], - countMatches: true,If match validation is still required, use a custom prepare step that accepts both "changed" and "already at base version" outcomes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.releaserc.js around lines 19 - 27, The replace step that writes "to: \"VERSION ${nextRelease.version.split('-')[0]}\"" can be a no-op when the CMakeLists.txt already contains the base version; update the semantic-release replace configuration to accept both outcomes by removing the strict results assertion or making the results entry permissive (allow hasChanged: false / numReplacements: 0) or implement a custom prepare step that treats "already at base version" as success; target the replace rule that contains the "to: \"VERSION ${nextRelease.version.split('-')[0]}\"" entry and adjust the results/validation logic used by the semantic-release-replace-plugin (currently enforcing isEqual at its validation) to not fail on zero replacements.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In @.releaserc.js:
- Around line 19-27: The replace step that writes "to: \"VERSION
${nextRelease.version.split('-')[0]}\"" can be a no-op when the CMakeLists.txt
already contains the base version; update the semantic-release replace
configuration to accept both outcomes by removing the strict results assertion
or making the results entry permissive (allow hasChanged: false /
numReplacements: 0) or implement a custom prepare step that treats "already at
base version" as success; target the replace rule that contains the "to:
\"VERSION ${nextRelease.version.split('-')[0]}\"" entry and adjust the
results/validation logic used by the semantic-release-replace-plugin (currently
enforcing isEqual at its validation) to not fail on zero replacements.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 90f73974-0615-416d-a123-359adaa3460f
📒 Files selected for processing (2)
.github/workflows/release.yaml.releaserc.js
When stable runs after RC, CMakeLists already has the base version (e.g. 1.5.0), making the replace a no-op. The strict results assertion (hasChanged: true, numReplacements: 1) causes the plugin to throw on mismatch. Drop it so stable-after-RC completes cleanly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.releaserc.js:
- Around line 18-21: Replace the string "from" in .releaserc.js with a callback
that reads the file contents (use fs.readFileSync) and first checks whether the
file already contains a stable VERSION pattern (e.g., /VERSION
\d+\.\d+\.\d+(?!-)/) to allow the no-op case; if that stable pattern is absent,
verify the prerelease/version pattern (/VERSION \d+\.\d+\.\d+/) exists and only
then return the regex (/VERSION [0-9]+\.[0-9]+\.[0-9]+/gm) to perform the
replacement, otherwise throw a clear error about the missing VERSION line so you
catch regex drift without breaking stable-after-RC no-ops.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a9b2324e-3274-4a03-bcc6-7baa03179595
📒 Files selected for processing (1)
.releaserc.js
| from: 'VERSION [0-9]+\\.[0-9]+\\.[0-9]+', | ||
| to: 'VERSION ${nextRelease.version}', | ||
| results: [ | ||
| { | ||
| file: 'CMakeLists.txt', | ||
| hasChanged: true, | ||
| numReplacements: 1, | ||
| numMatches: 1, | ||
| }, | ||
| ], | ||
| countMatches: true, | ||
| // Strip prerelease suffix so CMake gets '1.5.0' not '1.5.0-rc.1'. | ||
| // No results assertion: stable after RC is a no-op (version already set). | ||
| to: "VERSION ${nextRelease.version.split('-')[0]}", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Inspect release plugin/action version references in repo config and lock files.
# Expected: Find the semantic-release action and any pinned replace-plugin versions, if present.
rg -n -C3 '(`@google/semantic-release-replace-plugin`|semantic-release-replace-plugin|cycjimmy/semantic-release-action)' \
--iglob 'package*.json' \
--iglob 'pnpm-lock.yaml' \
--iglob 'yarn.lock' \
--iglob '*.yaml' \
--iglob '*.yml'Repository: doodlum/skyrim-community-shaders
Length of output: 58
🏁 Script executed:
# Check if .releaserc.js exists and view its content
find . -name ".releaserc.js" -o -name ".releaserc" -o -name "releaserc*" | head -20Repository: doodlum/skyrim-community-shaders
Length of output: 91
🏁 Script executed:
# Check package.json for semantic-release dependencies
fd -name 'package.json' -exec grep -l 'semantic-release\|release' {} \;Repository: doodlum/skyrim-community-shaders
Length of output: 306
🏁 Script executed:
# Look at the repository structure
ls -la . | head -20Repository: doodlum/skyrim-community-shaders
Length of output: 1300
🏁 Script executed:
# Read the .releaserc.js file
cat -n .releaserc.jsRepository: doodlum/skyrim-community-shaders
Length of output: 1386
🏁 Script executed:
# Check package.json for semantic-release dependencies
cat package.jsonRepository: doodlum/skyrim-community-shaders
Length of output: 120
🏁 Script executed:
# Search for package.json anywhere in the repo
find . -name 'package.json' -type fRepository: doodlum/skyrim-community-shaders
Length of output: 58
🏁 Script executed:
# Search for lock files
find . -name 'pnpm-lock.yaml' -o -name 'yarn.lock' -o -name 'package-lock.json' | head -5Repository: doodlum/skyrim-community-shaders
Length of output: 58
🏁 Script executed:
# Check for any dependency specifications in other config files
grep -r 'google.*semantic-release-replace-plugin' . --include='*.json' --include='*.yaml' --include='*.yml' 2>/dev/null | head -10Repository: doodlum/skyrim-community-shaders
Length of output: 170
🏁 Script executed:
# Read the release workflow to understand how the plugin is invoked
cat -n .github/workflows/release.yamlRepository: doodlum/skyrim-community-shaders
Length of output: 1751
🌐 Web query:
@google/semantic-release-replace-plugin from callback support API documentation
💡 Result:
The @google/semantic-release-replace-plugin (also published as semantic-release-replace-plugin) supports callback functions for both the 'from' and 'to' options in its Replacement configuration interface. This is an advanced feature primarily for use with release.config.js (JavaScript config files), as JSON configs cannot express functions. Key details from the official API documentation: - from: Can be From or From[], where From is string | RegExp | FromCallback. FromCallback is defined as (filename: string, ...args: unknown[]) => RegExp | string. Consult replace-in-file docs for advanced matching with callbacks, RegExps, etc. - to: Can be To or To[], where To is string | ToCallback. ToCallback is (match: string, ...args: unknown[]) => string. Callback args vary: - For simple string 'from', 'to' callback simplest form receives the filename. - For RegExp 'from', args include captures, offset, matched string, etc. (per String.replace docs). Example for 'to' callback (from docs): { from: VERSION = 11, to: (matched) => VERSION: ${parseInt(matched.split('=')[1].trim) + 1}, } The plugin uses replace-in-file under the hood, adapting its API (single from/to vs. arrays). Templates like ${nextRelease.version} are rendered first using semantic-release context. Note: Requires release.config.js for callbacks; package.json configs are limited to strings. Pair with @semantic-release/git to commit changes. Official sources: GitHub repo at https://github.com/jpoehnelt/semantic-release-replace-plugin (docs/interfaces/Replacement.md and types). NPM pages confirm @google/ namespace is the same package.
Citations:
- 1: https://github.com/jpoehnelt/semantic-release-replace-plugin/blob/main/docs/interfaces/Replacement.md
- 2: https://www.npmjs.com/package/@google/semantic-release-replace-plugin
- 3: https://github.com/jpoehnelt/semantic-release-replace-plugin/blob/main/docs/README.md
- 4: https://github.com/jpoehnelt/semantic-release-replace-plugin
- 5: https://www.npmjs.com/package/semantic-release-replace-plugin
The callback proposal has a critical flaw: it breaks no-op behavior for stable releases.
The concern is valid—dropping results validation lets regex drift silently—and @google/semantic-release-replace-plugin does support callback-based from options. However, the proposed guard would throw an error when the CMake version pattern is absent, which occurs on stable releases after RC (when the pattern was already replaced). The comment explicitly states "stable after RC is a no-op (version already set)," but the callback would fail instead of allowing that no-op state.
To preserve no-op replacements while catching regex drift, consider this approach instead:
Alternative guard preserving no-op behavior
Rather than asserting the match exists upfront, validate that the pattern was either replaced OR already matched the desired state:
from: (filename) => {
const contents = fs.readFileSync(filename, 'utf8');
const alreadyMatches = /VERSION \d+\.\d+\.\d+(?!-)/m.test(contents);
// Ensure pattern exists OR file already has desired version format (for no-op case)
if (!alreadyMatches && !/VERSION \d+\.\d+\.\d+/.test(contents)) {
throw new Error(`Missing CMake VERSION pattern in ${filename}`);
}
return /VERSION [0-9]+\.[0-9]+\.[0-9]+/gm;
},This allows the regex to match (replacing any prerelease version) while still catching if the VERSION line is missing entirely.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.releaserc.js around lines 18 - 21, Replace the string "from" in
.releaserc.js with a callback that reads the file contents (use fs.readFileSync)
and first checks whether the file already contains a stable VERSION pattern
(e.g., /VERSION \d+\.\d+\.\d+(?!-)/) to allow the no-op case; if that stable
pattern is absent, verify the prerelease/version pattern (/VERSION
\d+\.\d+\.\d+/) exists and only then return the regex (/VERSION
[0-9]+\.[0-9]+\.[0-9]+/gm) to perform the replacement, otherwise throw a clear
error about the missing VERSION line so you catch regex drift without breaking
stable-after-RC no-ops.
|
✅ A pre-release build is available for this PR: |
…shaders#2188) Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> (cherry picked from commit d9c4b91)
…shaders#2188) Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Prereq: 'main' branch must exist on remote (create from v1.4.11 tag).
Summary by CodeRabbit