-
Notifications
You must be signed in to change notification settings - Fork 0
feat: integrate Nx Release for version bumps and changelog updates #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughThe PR replaces custom Codex-driven version-bump scripts and inline workflow logic with an Nx Release–based flow. It adds Changes
Sequence DiagramsequenceDiagram
participant GH as GitHub Actions
participant Codex as Codex AI
participant NXR as nx-release.mjs (Nx Release)
participant Repo as Repo Files (package.json / CHANGELOGs)
participant Git as Git
GH->>Codex: Run analysis -> determine bumps & changelogs
Codex-->>GH: Return CODEX_OUTPUT (projects, changelogs)
GH->>NXR: Invoke `scripts/nx-release.mjs` with CODEX_OUTPUT
NXR->>NXR: Parse CODEX_OUTPUT, filter bumped projects
loop per bumped project
NXR->>Repo: Read current package.json (project)
NXR->>NXR: Compute/apply new version via releaseVersion (git disabled)
NXR->>Repo: Write updated package.json and CHANGELOG.md
NXR->>Git: Create commit & tag (per-project lib@version)
end
NXR->>Repo: Optionally update global CHANGELOG.md
NXR-->>GH: Emit JSON results (MAX_VERSION / BUMPED)
GH->>Git: Push commits and tags
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (6)
scripts/nx-release.mjs (3)
60-67: Unused destructured variableprojectsVersionData.The
projectsVersionDatais extracted but never used. Either remove it or use it to verify the version was applied correctly.- const { projectsVersionData } = await releaseVersion({ + await releaseVersion({ specifier: project.newVersion, projects: [project.name], dryRun, verbose: true, gitCommit: false, gitTag: false, });
153-155:sort()mutates the original array.While not currently causing issues,
sort()mutatesversionsin place. Consider usingtoSorted()or spreading to avoid surprises if the array is reused.- const maxVersion = versions.sort((a, b) => + const maxVersion = [...versions].sort((a, b) => b.localeCompare(a, undefined, { numeric: true, sensitivity: 'base' }) )[0];
79-90: Changelog insertion assumes specific format.The logic assumes the changelog has a
## [Unreleased]section. If missing, the entry is silently skipped. Consider logging a warning when the expected format isn't found.if (unreleasedIdx !== -1) { const afterUnreleased = content.indexOf('\n', unreleasedIdx) + 1; content = content.slice(0, afterUnreleased) + '\n' + entry + '\n' + content.slice(afterUnreleased); fs.writeFileSync(changelogPath, content); console.log(`✓ Updated changelog: ${changelogPath}`); + } else { + console.log(`⚠ Skipped changelog update for ${project.name}: missing "## [Unreleased]" section`); }.github/workflows/create-release-branch.yml (2)
247-259: Consider using script output instead of re-parsing Codex output.The
nx-release.mjsscript outputs__VERSION_RESULTS_JSON__(lines 113-114) with version results, but this workflow re-parses the Codex output file to extract the same information. Consider capturing the script's output to avoid duplication:# Run Nx Release script to bump versions and update changelogs - node scripts/nx-release.mjs - - # Extract max version and bumped projects from Codex output - RESULT=$(node -e " - const fs = require('fs'); - const output = JSON.parse(fs.readFileSync('${{ env.CODEX_OUTPUT }}', 'utf8')); - const bumped = output.projects.filter(p => p.bump !== 'none'); - const maxVersion = bumped.map(p => p.newVersion) - .sort((a,b) => b.localeCompare(a, undefined, {numeric: true}))[0] || '0.0.0'; - const bumpedProjects = bumped.map(p => p.name).join(','); - console.log(JSON.stringify({ maxVersion, bumpedProjects })); - ") + SCRIPT_OUTPUT=$(node scripts/nx-release.mjs) + + # Extract version results from script output + VERSION_JSON=$(echo "$SCRIPT_OUTPUT" | grep -A1 "__VERSION_RESULTS_JSON__" | tail -1) + + # Compute max version and bumped projects + RESULT=$(node -e " + const data = JSON.parse('$VERSION_JSON'); + const entries = Object.entries(data.versionResults); + const maxVersion = entries.map(([,v]) => v) + .sort((a,b) => b.localeCompare(a, undefined, {numeric: true}))[0] || '0.0.0'; + const bumpedProjects = entries.map(([n]) => n).join(','); + console.log(JSON.stringify({ maxVersion, bumpedProjects })); + ")Alternatively, the script could write results to a file that the workflow reads.
269-270: RedundantCODEX_OUTPUTenvironment variable.
CODEX_OUTPUTis already defined at the job level (line 17). This step-level declaration is harmless but unnecessary.- env: - CODEX_OUTPUT: ${{ env.CODEX_OUTPUT }}.github/workflows/publish-on-next-close.yml (1)
131-148: Unused "Determine release version" step—consider removal.The "Determine release version" step (lines 131–148) reads a workspace-level version and stores it in
steps.version.outputs.version, but this output is never referenced elsewhere in the workflow. The summary (lines 247–263) and tagging sections (lines 172–198) both read per-project versions independently from each library'spackage.json.This step is likely remnant from the previous global-release workflow. Consider removing it to simplify the workflow, since per-project versioning is now the standard approach.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
.github/workflows/create-release-branch.yml(3 hunks).github/workflows/publish-on-next-close.yml(1 hunks)libs/ast-guard/project.json(1 hunks)libs/enclave-vm/project.json(1 hunks)libs/vectoriadb/project.json(1 hunks)nx.json(1 hunks)scripts/analyze-version-bump.mjs(0 hunks)scripts/bump-synchronized-versions.mjs(0 hunks)scripts/bump-version.mjs(0 hunks)scripts/nx-release.mjs(1 hunks)
💤 Files with no reviewable changes (3)
- scripts/bump-synchronized-versions.mjs
- scripts/bump-version.mjs
- scripts/analyze-version-bump.mjs
🧰 Additional context used
📓 Path-based instructions (1)
libs/**
⚙️ CodeRabbit configuration file
libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).
Files:
libs/ast-guard/project.jsonlibs/vectoriadb/project.jsonlibs/enclave-vm/project.json
🪛 GitHub Actions: CI
scripts/nx-release.mjs
[warning] 1-1: Prettier formatting issue detected in this file. Run 'npx prettier --write scripts/nx-release.mjs' to fix.
🔇 Additional comments (9)
nx.json (2)
83-99: Release configuration looks well-structured for independent versioning.The configuration properly sets up:
- Independent project relationships with per-project tagging (
{projectName}@{version})- Disabled conventional commits (delegated to Codex AI)
- Disabled Nx-generated changelogs (handled by Codex)
- Pre-version build command to ensure artifacts are ready
90-94: Thegitblock in nx.json is unused and can be safely removed.The
commitMessagetemplate at line 93 is never executed becausescripts/nx-release.mjsdisables git operations (gitCommit: false,gitTag: false). The workflow handles all git commits and tags separately using hardcoded messages (e.g.,"chore(release): prepare release v$MAX_VERSION"increate-release-branch.yml), not the template defined in nx.json.Either remove the block entirely to reduce confusion, or document it as reserved for future direct
nx releaseCLI usage.libs/vectoriadb/project.json (1)
25-31: Migration to@nx/js:release-publishlooks correct.The target properly:
- Uses the official Nx Release executor
- Points to the correct dist output path
- Maintains the build dependency to ensure artifacts exist before publishing
libs/enclave-vm/project.json (1)
26-32: Consistent migration to@nx/js:release-publish.Configuration mirrors the vectoriadb pattern and correctly maintains the build dependency. The
implicitDependenciesonast-guard(line 7) ensures proper ordering if both packages are released together..github/workflows/create-release-branch.yml (1)
230-248: Nx Release integration step looks well-structured.The step properly:
- Validates Codex output file existence
- Logs the output for debugging
- Delegates version bumping to the dedicated script
- Extracts results for downstream steps
libs/ast-guard/project.json (1)
25-31: Verify@nx/js:release-publishexecutor configuration.The target uses
@nx/js:release-publishexecutor withpackageRootoption. Please confirm this executor and option name are valid in your Nx version and that the dist path is correct for npm publishing..github/workflows/publish-on-next-close.yml (3)
172-198: Verify version pre-existence assumption in package.json files.This workflow reads versions directly from
libs/<lib>/package.jsonfiles to create git tags. Please confirm that version bumping happens in an earlier workflow step/action (likely viacreate-release-branchworkflow mentioned in the PR context) before thispublish-on-next-closeworkflow executes. If version updates are meant to happen during this workflow, the sequencing needs adjustment.
204-210: Nx Release publish invocation looks correct.The
npx nx release publishcommand properly targets the filtered list of affected publishable projects. The conditional guard ensures nothing runs if projects list is empty.
212-245: Per-project release creation approach is sound; verify tag dependency.The per-project release creation iterates correctly through the projects list, reads versions, and uses
gh release createwith--generate-notesto supplement the minimal per-project body. The error suppression with|| echois acceptable for handling idempotent release creation (releases may already exist).Confirm that the git tags created in the "Create git tags (per-project)" step (lines 172–198) are available to
gh release createwhen this step executes—the push to origin (line 196) should ensure this, but verify tags are visible before release creation attempts begin.
| @@ -0,0 +1,179 @@ | |||
| /** | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Address Prettier formatting issue.
The CI pipeline flagged a formatting issue. Run npx prettier --write scripts/nx-release.mjs to fix.
🧰 Tools
🪛 GitHub Actions: CI
[warning] 1-1: Prettier formatting issue detected in this file. Run 'npx prettier --write scripts/nx-release.mjs' to fix.
🤖 Prompt for AI Agents
In scripts/nx-release.mjs around line 1, the file fails Prettier formatting in
CI; run the formatter and commit the result. Execute `npx prettier --write
scripts/nx-release.mjs`, review changes, stage and commit the updated file so
the codebase matches Prettier formatting rules.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (1)
.github/workflows/publish-on-next-close.yml (1)
193-226: Improve error handling for GitHub release creation.The
|| echofallback on line 224 masks all errors fromgh release create, including network failures and authentication issues. Consider distinguishing between "already exists" and other errors.Add explicit error checking:
# Create release using gh CLI - gh release create "$TAG_NAME" \ - --title "$TAG_NAME" \ - --notes "$BODY" \ - --generate-notes \ - || echo "Release $TAG_NAME may already exist, skipping..." + if gh release view "$TAG_NAME" >/dev/null 2>&1; then + echo "Release $TAG_NAME already exists, skipping..." + else + gh release create "$TAG_NAME" \ + --title "$TAG_NAME" \ + --notes "$BODY" \ + --generate-notes + fi
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
.github/workflows/publish-on-next-close.yml(1 hunks)scripts/nx-release.mjs(1 hunks)
🔇 Additional comments (9)
scripts/nx-release.mjs (7)
19-31: LGTM! Environment validation is thorough.The script properly validates required environment variables and file existence with clear error messages before proceeding.
36-47: LGTM! Error handling improvements implemented.The JSON parsing and projects array validation now include proper error handling as suggested in previous reviews. The error messages are clear and the script exits gracefully on failure.
49-61: LGTM! Initialization and filtering logic is sound.The script correctly filters projects needing version bumps and handles the no-op case gracefully with an early exit.
109-112: LGTM! Global changelog integration is properly gated.The global changelog update is correctly conditioned on both the presence of the data and the dry-run flag.
114-124: LGTM! Results output format is CI-friendly.The dual output (human-readable log + JSON) provides good observability while making results easily consumable by downstream CI steps.
126-149: LGTM! Changelog formatting follows standard conventions.The function correctly implements Keep a Changelog category formatting and handles empty changelogs by returning null.
184-187: LGTM! Top-level error handler is appropriate.The catch handler provides a safety net for unhandled errors, ensuring the script exits with a non-zero code for CI detection.
.github/workflows/publish-on-next-close.yml (2)
228-244: LGTM! Summary generation provides useful feedback.The summary step provides clear visibility into what was published and the corresponding release tags. While the version extraction could benefit from error handling (similar to earlier steps), failures here are not critical since publishing has already succeeded.
185-191: No action required. All publishable projects already havenx-release-publishtargets configured in their project.json files with the@nx/js:release-publishexecutor, and the Nx Release configuration in nx.json is properly set up to support thenpx nx release publishcommand.
| IFS=',' read -ra LIBS <<< "$PROJECTS" | ||
| for lib in "${LIBS[@]}"; do | ||
| # Read project version from package.json | ||
| LIB_VERSION=$(node -e "const pkg = require('./libs/$lib/package.json'); console.log(pkg.version);") | ||
| PROJECT_TAG="${lib}@${LIB_VERSION}" | ||
| if git rev-parse "$PROJECT_TAG" >/dev/null 2>&1; then | ||
| echo "Tag $PROJECT_TAG already exists." | ||
| else | ||
| echo "Creating project tag $PROJECT_TAG at $TARGET_SHA" | ||
| git tag -a "$PROJECT_TAG" "$TARGET_SHA" -m "Release $PROJECT_TAG" | ||
| git push origin "$PROJECT_TAG" | ||
| fi | ||
| done |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add error handling for package.json version extraction.
If a project's package.json is missing or malformed, the version extraction will fail silently or produce an empty/invalid tag name.
Add validation to ensure the version is successfully extracted:
IFS=',' read -ra LIBS <<< "$PROJECTS"
for lib in "${LIBS[@]}"; do
# Read project version from package.json
- LIB_VERSION=$(node -e "const pkg = require('./libs/$lib/package.json'); console.log(pkg.version);")
+ LIB_VERSION=$(node -e "
+ try {
+ const pkg = require('./libs/$lib/package.json');
+ if (!pkg.version) {
+ console.error('No version field in package.json');
+ process.exit(1);
+ }
+ console.log(pkg.version);
+ } catch (err) {
+ console.error('Failed to read package.json:', err.message);
+ process.exit(1);
+ }
+ ")
+
+ if [ -z "$LIB_VERSION" ]; then
+ echo "Error: Failed to extract version for $lib"
+ exit 1
+ fi
PROJECT_TAG="${lib}@${LIB_VERSION}"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| IFS=',' read -ra LIBS <<< "$PROJECTS" | |
| for lib in "${LIBS[@]}"; do | |
| # Read project version from package.json | |
| LIB_VERSION=$(node -e "const pkg = require('./libs/$lib/package.json'); console.log(pkg.version);") | |
| PROJECT_TAG="${lib}@${LIB_VERSION}" | |
| if git rev-parse "$PROJECT_TAG" >/dev/null 2>&1; then | |
| echo "Tag $PROJECT_TAG already exists." | |
| else | |
| echo "Creating project tag $PROJECT_TAG at $TARGET_SHA" | |
| git tag -a "$PROJECT_TAG" "$TARGET_SHA" -m "Release $PROJECT_TAG" | |
| git push origin "$PROJECT_TAG" | |
| fi | |
| done | |
| IFS=',' read -ra LIBS <<< "$PROJECTS" | |
| for lib in "${LIBS[@]}"; do | |
| # Read project version from package.json | |
| LIB_VERSION=$(node -e " | |
| try { | |
| const pkg = require('./libs/$lib/package.json'); | |
| if (!pkg.version) { | |
| console.error('No version field in package.json'); | |
| process.exit(1); | |
| } | |
| console.log(pkg.version); | |
| } catch (err) { | |
| console.error('Failed to read package.json:', err.message); | |
| process.exit(1); | |
| } | |
| ") | |
| if [ -z "$LIB_VERSION" ]; then | |
| echo "Error: Failed to extract version for $lib" | |
| exit 1 | |
| fi | |
| PROJECT_TAG="${lib}@${LIB_VERSION}" | |
| if git rev-parse "$PROJECT_TAG" >/dev/null 2>&1; then | |
| echo "Tag $PROJECT_TAG already exists." | |
| else | |
| echo "Creating project tag $PROJECT_TAG at $TARGET_SHA" | |
| git tag -a "$PROJECT_TAG" "$TARGET_SHA" -m "Release $PROJECT_TAG" | |
| git push origin "$PROJECT_TAG" | |
| fi | |
| done |
🤖 Prompt for AI Agents
In .github/workflows/publish-on-next-close.yml around lines 165 to 179, the
script extracts package versions with a node one-liner but does not validate the
result; if package.json is missing/malformed the extraction can fail or yield an
empty/invalid tag. Change the flow so the node command output and exit status
are captured into LIB_VERSION, then validate LIB_VERSION is non-empty and
matches a semantic-version pattern (e.g. simple regex like
^[0-9]+\.[0-9]+\.[0-9]+(-.*)?$); if extraction fails or validation fails, log a
clear error including the lib name and skip tagging that lib (or exit non-zero
if desired), otherwise proceed to create and push the tag. Ensure errors from
node are surfaced (check exit code) and do not produce empty/garbage tag names.
| // Apply Codex-generated changelog (Nx doesn't handle this) | ||
| if (project.changelog && !dryRun) { | ||
| const changelogPath = path.join('libs', project.name, 'CHANGELOG.md'); | ||
| if (fs.existsSync(changelogPath)) { | ||
| const entry = formatChangelogEntry(project.newVersion, project.changelog, today); | ||
| if (entry) { | ||
| let content = fs.readFileSync(changelogPath, 'utf8'); | ||
| const unreleasedIdx = content.indexOf('## [Unreleased]'); | ||
| if (unreleasedIdx !== -1) { | ||
| const afterUnreleased = content.indexOf('\n', unreleasedIdx) + 1; | ||
| content = content.slice(0, afterUnreleased) + '\n' + entry + '\n' + content.slice(afterUnreleased); | ||
| fs.writeFileSync(changelogPath, content); | ||
| console.log(`✓ Updated changelog: ${changelogPath}`); | ||
| } else { | ||
| console.log(`⚠ Skipped changelog update for ${project.name}: missing "## [Unreleased]" section`); | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add error handling for changelog filesystem operations.
The changelog update logic (readFileSync, writeFileSync) can throw errors (e.g., EACCES, ENOSPC) that would crash the script. Since the main version bumping succeeded before this point, the script should handle these errors gracefully rather than exiting.
Consider wrapping the changelog operations in a try-catch:
// Apply Codex-generated changelog (Nx doesn't handle this)
if (project.changelog && !dryRun) {
- const changelogPath = path.join('libs', project.name, 'CHANGELOG.md');
- if (fs.existsSync(changelogPath)) {
- const entry = formatChangelogEntry(project.newVersion, project.changelog, today);
- if (entry) {
- let content = fs.readFileSync(changelogPath, 'utf8');
- const unreleasedIdx = content.indexOf('## [Unreleased]');
- if (unreleasedIdx !== -1) {
- const afterUnreleased = content.indexOf('\n', unreleasedIdx) + 1;
- content = content.slice(0, afterUnreleased) + '\n' + entry + '\n' + content.slice(afterUnreleased);
- fs.writeFileSync(changelogPath, content);
- console.log(`✓ Updated changelog: ${changelogPath}`);
- } else {
- console.log(`⚠ Skipped changelog update for ${project.name}: missing "## [Unreleased]" section`);
+ try {
+ const changelogPath = path.join('libs', project.name, 'CHANGELOG.md');
+ if (fs.existsSync(changelogPath)) {
+ const entry = formatChangelogEntry(project.newVersion, project.changelog, today);
+ if (entry) {
+ let content = fs.readFileSync(changelogPath, 'utf8');
+ const unreleasedIdx = content.indexOf('## [Unreleased]');
+ if (unreleasedIdx !== -1) {
+ const afterUnreleased = content.indexOf('\n', unreleasedIdx) + 1;
+ content = content.slice(0, afterUnreleased) + '\n' + entry + '\n' + content.slice(afterUnreleased);
+ fs.writeFileSync(changelogPath, content);
+ console.log(`✓ Updated changelog: ${changelogPath}`);
+ } else {
+ console.log(`⚠ Skipped changelog update for ${project.name}: missing "## [Unreleased]" section`);
+ }
}
}
+ } catch (err) {
+ console.error(`⚠ Failed to update changelog for ${project.name}: ${err.message}`);
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Apply Codex-generated changelog (Nx doesn't handle this) | |
| if (project.changelog && !dryRun) { | |
| const changelogPath = path.join('libs', project.name, 'CHANGELOG.md'); | |
| if (fs.existsSync(changelogPath)) { | |
| const entry = formatChangelogEntry(project.newVersion, project.changelog, today); | |
| if (entry) { | |
| let content = fs.readFileSync(changelogPath, 'utf8'); | |
| const unreleasedIdx = content.indexOf('## [Unreleased]'); | |
| if (unreleasedIdx !== -1) { | |
| const afterUnreleased = content.indexOf('\n', unreleasedIdx) + 1; | |
| content = content.slice(0, afterUnreleased) + '\n' + entry + '\n' + content.slice(afterUnreleased); | |
| fs.writeFileSync(changelogPath, content); | |
| console.log(`✓ Updated changelog: ${changelogPath}`); | |
| } else { | |
| console.log(`⚠ Skipped changelog update for ${project.name}: missing "## [Unreleased]" section`); | |
| } | |
| } | |
| } | |
| } | |
| // Apply Codex-generated changelog (Nx doesn't handle this) | |
| if (project.changelog && !dryRun) { | |
| try { | |
| const changelogPath = path.join('libs', project.name, 'CHANGELOG.md'); | |
| if (fs.existsSync(changelogPath)) { | |
| const entry = formatChangelogEntry(project.newVersion, project.changelog, today); | |
| if (entry) { | |
| let content = fs.readFileSync(changelogPath, 'utf8'); | |
| const unreleasedIdx = content.indexOf('## [Unreleased]'); | |
| if (unreleasedIdx !== -1) { | |
| const afterUnreleased = content.indexOf('\n', unreleasedIdx) + 1; | |
| content = content.slice(0, afterUnreleased) + '\n' + entry + '\n' + content.slice(afterUnreleased); | |
| fs.writeFileSync(changelogPath, content); | |
| console.log(`✓ Updated changelog: ${changelogPath}`); | |
| } else { | |
| console.log(`⚠ Skipped changelog update for ${project.name}: missing "## [Unreleased]" section`); | |
| } | |
| } | |
| } | |
| } catch (err) { | |
| console.error(`⚠ Failed to update changelog for ${project.name}: ${err.message}`); | |
| } | |
| } |
🤖 Prompt for AI Agents
In scripts/nx-release.mjs around lines 84 to 102, the changelog read/write
operations are unprotected and can throw (EACCES, ENOSPC, etc.), which could
crash the release script after version bump; wrap the block that reads, formats,
and writes the changelog in a try-catch, and on error log a clear warning
including project.name and the error.message/stack, then continue without
rethrowing so the script does not exit; keep the existing existence check and
dryRun behavior, but ensure any fs.readFileSync/fs.writeFileSync and
formatChangelogEntry use are inside the try block.
| function updateGlobalChangelog(globalChangelog, versionResults, date) { | ||
| const globalPath = 'CHANGELOG.md'; | ||
| if (!fs.existsSync(globalPath)) { | ||
| console.log('Global CHANGELOG.md not found, skipping'); | ||
| return; | ||
| } | ||
|
|
||
| const versions = Object.values(versionResults); | ||
| if (versions.length === 0) return; | ||
|
|
||
| // Find max version (spread to avoid mutating original array) | ||
| const maxVersion = [...versions].sort((a, b) => | ||
| b.localeCompare(a, undefined, { numeric: true, sensitivity: 'base' }), | ||
| )[0]; | ||
|
|
||
| let content = fs.readFileSync(globalPath, 'utf8'); | ||
|
|
||
| let globalEntry = `## [${maxVersion}] - ${date}\n\n`; | ||
| globalEntry += globalChangelog.summary + '\n\n'; | ||
| globalEntry += '### Updated Libraries\n\n'; | ||
| for (const p of globalChangelog.projects || []) { | ||
| globalEntry += `- **${p.name}** v${p.version} - ${p.summary}\n`; | ||
| } | ||
|
|
||
| const unreleasedIdx = content.indexOf('## [Unreleased]'); | ||
| if (unreleasedIdx !== -1) { | ||
| const afterUnreleased = content.indexOf('\n', unreleasedIdx) + 1; | ||
| content = content.slice(0, afterUnreleased) + '\n' + globalEntry + '\n' + content.slice(afterUnreleased); | ||
| fs.writeFileSync(globalPath, content); | ||
| console.log('✓ Updated global changelog'); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add error handling for global changelog filesystem operations.
Similar to the per-project changelog updates, the global changelog update can fail due to filesystem errors. Since this runs after successful version bumps, failures should be logged as warnings rather than crashing the script.
Wrap the filesystem operations in a try-catch:
function updateGlobalChangelog(globalChangelog, versionResults, date) {
const globalPath = 'CHANGELOG.md';
if (!fs.existsSync(globalPath)) {
console.log('Global CHANGELOG.md not found, skipping');
return;
}
const versions = Object.values(versionResults);
if (versions.length === 0) return;
- // Find max version (spread to avoid mutating original array)
- const maxVersion = [...versions].sort((a, b) =>
- b.localeCompare(a, undefined, { numeric: true, sensitivity: 'base' }),
- )[0];
-
- let content = fs.readFileSync(globalPath, 'utf8');
-
- let globalEntry = `## [${maxVersion}] - ${date}\n\n`;
- globalEntry += globalChangelog.summary + '\n\n';
- globalEntry += '### Updated Libraries\n\n';
- for (const p of globalChangelog.projects || []) {
- globalEntry += `- **${p.name}** v${p.version} - ${p.summary}\n`;
- }
-
- const unreleasedIdx = content.indexOf('## [Unreleased]');
- if (unreleasedIdx !== -1) {
- const afterUnreleased = content.indexOf('\n', unreleasedIdx) + 1;
- content = content.slice(0, afterUnreleased) + '\n' + globalEntry + '\n' + content.slice(afterUnreleased);
- fs.writeFileSync(globalPath, content);
- console.log('✓ Updated global changelog');
+ try {
+ // Find max version (spread to avoid mutating original array)
+ const maxVersion = [...versions].sort((a, b) =>
+ b.localeCompare(a, undefined, { numeric: true, sensitivity: 'base' }),
+ )[0];
+
+ let content = fs.readFileSync(globalPath, 'utf8');
+
+ let globalEntry = `## [${maxVersion}] - ${date}\n\n`;
+ globalEntry += globalChangelog.summary + '\n\n';
+ globalEntry += '### Updated Libraries\n\n';
+ for (const p of globalChangelog.projects || []) {
+ globalEntry += `- **${p.name}** v${p.version} - ${p.summary}\n`;
+ }
+
+ const unreleasedIdx = content.indexOf('## [Unreleased]');
+ if (unreleasedIdx !== -1) {
+ const afterUnreleased = content.indexOf('\n', unreleasedIdx) + 1;
+ content = content.slice(0, afterUnreleased) + '\n' + globalEntry + '\n' + content.slice(afterUnreleased);
+ fs.writeFileSync(globalPath, content);
+ console.log('✓ Updated global changelog');
+ }
+ } catch (err) {
+ console.error(`⚠ Failed to update global changelog: ${err.message}`);
}
}🤖 Prompt for AI Agents
In scripts/nx-release.mjs around lines 151 to 182, the global changelog file
reads/writes are unprotected and can throw filesystem errors; wrap the
fs.existsSync, readFileSync and writeFileSync operations in a try-catch block so
failures do not crash the release script, log a warning (not an error/exit) when
an exception occurs, and return gracefully; ensure the catch includes the error
details in the warning message and preserve current behavior of skipping when
CHANGELOG.md is missing.
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.