Skip to content

Conversation

@frontegg-david
Copy link
Contributor

@frontegg-david frontegg-david commented Dec 12, 2025

Summary by CodeRabbit

  • Chores
    • Switched release automation to an Nx Release-based flow, enabling per-project publishing and per-project release tags instead of a single global version.
    • Simplified publishing and tagging logic and removed legacy manual version-bump scripts.
    • Added release configuration and orchestration to generate minimal per-project release notes and expose per-project release outputs.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 12, 2025

Walkthrough

The PR replaces custom Codex-driven version-bump scripts and inline workflow logic with an Nx Release–based flow. It adds scripts/nx-release.mjs, updates GitHub Actions to invoke it, switches project publish targets to @nx/js:release-publish, extends nx.json release settings, and removes older bumping scripts.

Changes

Cohort / File(s) Summary
Workflow orchestration
.github/workflows/create-release-branch.yml, .github/workflows/publish-on-next-close.yml
Replace inline Codex bump logic with invocation of scripts/nx-release.mjs; propagate CODEX_OUTPUT; expose MAX_VERSION/BUMPED outputs; change release flow to per-project tags and Nx Release publishing; create per-project GitHub Releases via gh.
Release scripts added/removed
scripts/nx-release.mjs, scripts/analyze-version-bump.mjs, scripts/bump-synchronized-versions.mjs, scripts/bump-version.mjs
Add nx-release.mjs to apply Codex results via Nx Release and update changelogs; remove three legacy version-bump utilities that analyzed diffs and mutated package versions.
Project targets updated
libs/ast-guard/project.json, libs/enclave-vm/project.json, libs/vectoriadb/project.json
Replace publish target (nx:run-commands + npm publish command) with nx-release-publish target using @nx/js:release-publish and packageRoot option.
Nx configuration
nx.json
Add release configuration: releaseTagPattern, independent project relationships, version/git settings (commitMessage template, preVersionCommand), and changelog flags; disable conventionalCommits as configured.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Review focus:
    • scripts/nx-release.mjs logic (parsing CODEX_OUTPUT, releaseVersion usage, changelog writes, error handling)
    • Workflow changes in both .github/workflows/*—outputs and environment propagation
    • nx.json release configuration for correctness with Nx Release
    • Confirm removal of old scripts doesn't leave dangling callers

Possibly related PRs

Poem

🐰 I hopped from Codex fields to Nx Release streams,
per-project tags twinkling like patchwork dreams.
Old scripts tucked in burrows, new script takes flight,
changelogs whisper stories beneath the moonlight. ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: integrating Nx Release for version bumps and changelog updates. This is directly reflected across multiple file changes (workflow updates, project.json changes, and new nx-release.mjs script).
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-workflow-publish

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

Copy link

@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: 3

🧹 Nitpick comments (6)
scripts/nx-release.mjs (3)

60-67: Unused destructured variable projectsVersionData.

The projectsVersionData is 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() mutates versions in place. Consider using toSorted() 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.mjs script 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: Redundant CODEX_OUTPUT environment variable.

CODEX_OUTPUT is 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's package.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

📥 Commits

Reviewing files that changed from the base of the PR and between a02454b and 20cbd47.

📒 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.json
  • libs/vectoriadb/project.json
  • libs/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: The git block in nx.json is unused and can be safely removed.

The commitMessage template at line 93 is never executed because scripts/nx-release.mjs disables 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" in create-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 release CLI usage.

libs/vectoriadb/project.json (1)

25-31: Migration to @nx/js:release-publish looks 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 implicitDependencies on ast-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-publish executor configuration.

The target uses @nx/js:release-publish executor with packageRoot option. 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.json files to create git tags. Please confirm that version bumping happens in an earlier workflow step/action (likely via create-release-branch workflow mentioned in the PR context) before this publish-on-next-close workflow 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 publish command 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 create with --generate-notes to supplement the minimal per-project body. The error suppression with || echo is 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 create when 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 @@
/**
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

Copy link

@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: 3

🧹 Nitpick comments (1)
.github/workflows/publish-on-next-close.yml (1)

193-226: Improve error handling for GitHub release creation.

The || echo fallback on line 224 masks all errors from gh 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

📥 Commits

Reviewing files that changed from the base of the PR and between 20cbd47 and 94d9306.

📒 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 have nx-release-publish targets configured in their project.json files with the @nx/js:release-publish executor, and the Nx Release configuration in nx.json is properly set up to support the npx nx release publish command.

Comment on lines +165 to +179
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
Copy link

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.

Suggested change
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.

Comment on lines +84 to +102
// 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`);
}
}
}
}
Copy link

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.

Suggested change
// 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.

Comment on lines +151 to +182
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');
}
}
Copy link

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.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants