Skip to content

fix(update-system): defensive VERSION parsing for release-please marker#547

Open
nhardgrove wants to merge 1 commit intosantifer:mainfrom
nhardgrove:fix/release-please-bump-version
Open

fix(update-system): defensive VERSION parsing for release-please marker#547
nhardgrove wants to merge 1 commit intosantifer:mainfrom
nhardgrove:fix/release-please-bump-version

Conversation

@nhardgrove
Copy link
Copy Markdown

@nhardgrove nhardgrove commented Apr 30, 2026

Summary

  • Release-please updates .release-please-manifest.json and CHANGELOG.md but leaves VERSION pinned at 1.3.0 across every release. The latest tag is v1.6.0, but main:VERSION still reads 1.3.0.
  • This breaks update-system.mjs: every apply ends with vX.Y.Z → vX.Y.Z because it reads the upstream VERSION file after checkout, and the checker keeps reporting update-available 1.3.0 → 1.6.0 indefinitely (it falls back to the GitHub release tag at lines 218-222 when VERSION is stale).
  • Fix: configure extra-files so release-please bumps VERSION during the release PR. The marker comment release-please requires for generic files is added, and update-system.mjs parses around it.

Repro

git clone https://github.com/santifer/career-ops.git && cd career-ops
cat VERSION                         # 1.3.0
cat .release-please-manifest.json   # {".": "1.6.0"}
git tag --sort=-v:refname | head -1 # v1.6.0
node update-system.mjs check        # update-available 1.3.0 → 1.6.0
node update-system.mjs apply        # Update complete: v1.3.0 → v1.3.0
node update-system.mjs check        # still update-available 1.3.0 → 1.6.0

Changes

  • .github/workflows/release.yml: add extra-files: VERSION so release-please-action@v4 rewrites the version on each release PR.
  • VERSION: bump to 1.6.0 (backfill) and append the x-release-please-version marker required by release-please for plain-text extra files.
  • update-system.mjs: extract parseVersionFile() and use it for both the local file read and the upstream raw fetch, so the marker doesn't break semver parsing.

Test plan

  • node update-system.mjs check returns up-to-date after the bump
  • parseVersionFile("1.6.0 # x-release-please-version\n")"1.6.0"
  • Maintainer: verify release-please's next release PR includes a VERSION change
  • Maintainer: confirm CI (test-all.mjs) passes against the modified update-system.mjs

Notes

Existing installs pulling this commit will jump to 1.6.0 immediately; subsequent release PRs will bump VERSION automatically. No breaking change for downstream — parseVersionFile is whitespace-tolerant and falls back cleanly when the marker is absent.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Fixed version detection in the update system to properly handle and parse version information, preventing release markers from interfering with accurate version comparisons during updates.

@github-actions
Copy link
Copy Markdown
Contributor

Welcome to career-ops, @nhardgrove! Thanks for your first PR.

A few things to know:

  • Tests will run automatically — check the status below
  • Make sure you've linked a related issue (required for features)
  • Read CONTRIBUTING.md if you haven't

We'll review your PR soon. Join our Discord if you have questions.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 30, 2026

📝 Walkthrough

Walkthrough

The update script introduces a parseVersionFile helper function that normalizes VERSION file contents by extracting the first whitespace-delimited token. This normalization is applied to both local and remote version parsing, preventing release-please marker suffixes from interfering with semver extraction.

Changes

Cohort / File(s) Summary
VERSION Parsing Normalization
update-system.mjs
Added parseVersionFile helper to strip release-please suffixes by tokenizing VERSION file contents; applied in localVersion() and remote version handling in check() flow.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~5 minutes

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and specifically describes the main change: adding defensive VERSION file parsing to handle release-please markers.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/release.yml:
- Around line 19-20: Remove the unsupported extra-files input from the
release-please workflow (the extra-files: VERSION entry) and instead configure
release-please via the config-file input for release-please-action@v4; add with:
config-file: .release-please-config.json to the action invocation and
create/update .release-please-config.json to list VERSION under the extraFiles
field so the VERSION file gets included in release PRs.

In `@VERSION`:
- Line 1: VERSION now contains an inline release-please marker which breaks the
strict semver check in test-all.mjs; update the validator in test-all.mjs to
ignore an inline marker by splitting the VERSION line at the first '#' (or
trimming any trailing " # x-release-please-version") and validating only the
part before the marker as semver (or update the regex to allow an optional " #
..." suffix). Locate the semver validation code in test-all.mjs (the VERSION
check) and change it to parse/trim off the marker before calling the semver
test.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: abfef407-0ff6-4515-bf6b-4faeb2d643d7

📥 Commits

Reviewing files that changed from the base of the PR and between 62b767d and 15302a8.

📒 Files selected for processing (3)
  • .github/workflows/release.yml
  • VERSION
  • update-system.mjs

Comment thread .github/workflows/release.yml Outdated
Comment thread VERSION Outdated
@@ -1 +1 @@
1.3.0
1.6.0 # x-release-please-version
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Update VERSION validator to handle the release-please marker

Line 1 now includes an inline marker, but test-all.mjs (Lines 302-315) still validates the entire line as strict semver. That will fail after this change.

Proposed fix (in test-all.mjs)
-  const version = readFile('VERSION').trim();
-  if (/^\d+\.\d+\.\d+$/.test(version)) {
+  const versionRaw = readFile('VERSION').trim();
+  const version = versionRaw.split(/\s+/)[0] || '';
+  if (/^\d+\.\d+\.\d+$/.test(version)) {
     pass(`VERSION is valid semver: ${version}`);
   } else {
-    fail(`VERSION is not valid semver: "${version}"`);
+    fail(`VERSION is not valid semver: "${versionRaw}"`);
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@VERSION` at line 1, VERSION now contains an inline release-please marker
which breaks the strict semver check in test-all.mjs; update the validator in
test-all.mjs to ignore an inline marker by splitting the VERSION line at the
first '#' (or trimming any trailing " # x-release-please-version") and
validating only the part before the marker as semver (or update the regex to
allow an optional " # ..." suffix). Locate the semver validation code in
test-all.mjs (the VERSION check) and change it to parse/trim off the marker
before calling the semver test.

@santifer
Copy link
Copy Markdown
Owner

Hey @nhardgrove — heads-up: #507 (chorrell) acabó de mergearse y resuelve la parte estructural de este PR usando release-please-config.json externo con release-type: simple + extra-files JSONPath para package.json. Ese approach es estructuralmente superior al inline extra-files: VERSION porque también sincroniza package.json (que tu PR no abordaba) y mantiene la config en un archivo dedicado.

PERO tu parseVersionFile() helper en update-system.mjs es buena defensive code que #507 NO incluye. Sigue siendo valioso — el handling whitespace-tolerant + ignorar el marker x-release-please-version es robusto.

Si quieres, ¿podrías rebasear este PR contra current main manteniendo SOLO el cambio en update-system.mjs (drop el workflow + VERSION change que ya están en main)? Ese rebased PR fast-tracks merge.

git fetch origin main
git rebase origin/main
# Resolve conflicts: keep your update-system.mjs changes, drop workflow + VERSION
git push --force-with-lease

Si no tienes bandwidth, no problem — cierro este PR en 7 días con thank-you. Door open para futuras contribuciones. La parseVersionFile idea era buena.

Closes nothing technical — la mejora estructural ya en main vía #507. Tu defensive helper sigue siendo standalone valioso.

…marker

The local VERSION file and the upstream raw fetch are both passed
through .trim() and a strict semver regex before this change. If the
project ever adopts the release-please convention of appending an
x-release-please-version marker comment to plain-text version files
(e.g. "1.6.0 # x-release-please-version"), the existing parsers break:
.trim() returns the whole string, the SEMVER_RE no longer matches, and
.split('.').map(Number) yields NaN.

This change extracts a parseVersionFile() helper that strips trailing
whitespace and ignores anything after the first whitespace token, then
routes both the local read (localVersion) and the upstream fetch
(check) through it. Behavior for marker-less files is unchanged --
"1.6.0\n" still parses to "1.6.0". Behavior for marker-bearing files
becomes correct rather than silently broken.

Standalone defensive code; complements the structural release-please
fix in santifer#507. No workflow or VERSION file changes here -- those landed
already via santifer#507.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@nhardgrove nhardgrove force-pushed the fix/release-please-bump-version branch from 15302a8 to b611603 Compare May 1, 2026 16:45
@nhardgrove nhardgrove changed the title fix(release): bump VERSION via release-please extra-files fix(update-system): defensive VERSION parsing for release-please marker May 1, 2026
@nhardgrove
Copy link
Copy Markdown
Author

Rebased onto current main per your suggestion. Dropped the workflow + VERSION changes (#507 covers both, structurally cleaner). Kept only parseVersionFile() in update-system.mjs — single commit, 8/2 diff.

Verified locally against the marker-less VERSION in main:

$ node update-system.mjs check
{"status":"up-to-date","local":"1.6.0","remote":"1.6.0"}

Helper is whitespace-tolerant and falls back cleanly when no marker is present, so it remains a no-op for current main while protecting against any future change to the VERSION format. Updated the PR title to reflect the narrower scope.

Thanks for the steer toward #507 — the external release-please-config.json + JSONPath approach is a much better foundation than my inline extra-files. 🚀

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 `@update-system.mjs`:
- Around line 105-109: The VERSION validation in test-all.mjs must use the same
normalization as parseVersionFile to accept marker-suffixed VERSION strings;
update test-all.mjs so that when it reads the VERSION content it trims and
splits on whitespace and takes the first token (same logic as parseVersionFile)
before running the semver/regex checks (i.e., normalize the raw VERSION string
to raw.trim().split(/\s+/)[0] and then validate that token instead of the
unnormalized full string).
🪄 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: ASSERTIVE

Plan: Pro

Run ID: 613bfc5e-aed2-4fbf-a6b9-bbfb9633186c

📥 Commits

Reviewing files that changed from the base of the PR and between 15302a8 and b611603.

📒 Files selected for processing (1)
  • update-system.mjs

Comment thread update-system.mjs
Comment on lines +105 to +109
function parseVersionFile(raw) {
// VERSION may carry a release-please marker, e.g. "1.6.0 # x-release-please-version".
// Take the first whitespace-delimited token so the marker doesn't break semver parsing.
return raw.trim().split(/\s+/)[0] || '';
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Align VERSION validation in test-all.mjs with the new normalization

This parser now accepts marker-suffixed VERSION content, but test-all.mjs still requires strict trimmed semver and will fail when the marker is present. Please normalize there too (same first-token parsing) to avoid CI false failures.

Suggested follow-up diff (cross-file)
--- a/test-all.mjs
+++ b/test-all.mjs
@@
 if (fileExists('VERSION')) {
-  const version = readFile('VERSION').trim();
+  const version = readFile('VERSION').trim().split(/\s+/)[0] || '';
   if (/^\d+\.\d+\.\d+$/.test(version)) {
     pass(`VERSION is valid semver: ${version}`);
   } else {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@update-system.mjs` around lines 105 - 109, The VERSION validation in
test-all.mjs must use the same normalization as parseVersionFile to accept
marker-suffixed VERSION strings; update test-all.mjs so that when it reads the
VERSION content it trims and splits on whitespace and takes the first token
(same logic as parseVersionFile) before running the semver/regex checks (i.e.,
normalize the raw VERSION string to raw.trim().split(/\s+/)[0] and then validate
that token instead of the unnormalized full string).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants