Skip to content

Conversation

@kingston
Copy link
Collaborator

@kingston kingston commented May 8, 2025

Summary by CodeRabbit

  • Chores
    • Updated the workflow to enforce that all code changes have corresponding changesets before proceeding.
    • Added a script to validate that affected packages are properly staged for release and their contents match the declared files.
    • Removed an older pre-publish validation script and replaced it with a more targeted changeset check.
    • Enhanced the end-to-end test workflow by caching Playwright browser binaries to speed up test runs.
    • Restricted published files in a package to only essential files for cleaner package contents.
  • New Features
    • Introduced automated validation to ensure package consistency and changeset coverage in pull requests.

@vercel
Copy link

vercel bot commented May 8, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
baseplate-project-builder-web ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 8, 2025 1:28pm

@changeset-bot
Copy link

changeset-bot bot commented May 8, 2025

⚠️ No Changeset found

Latest commit: ae900bd

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link

coderabbitai bot commented May 8, 2025

Walkthrough

The changes introduce a new validation workflow for npm packages in a monorepo. A new script, check-changesets.ts, is added and integrated into the CI pipeline to ensure affected packages have corresponding changesets and that their published contents match declarations. The older pre-publish.ts script is removed, and a new npm script is registered for the new check. Additionally, Playwright browser caching is added to the E2E test workflow, and the package files list is restricted in one package.

Changes

File(s) Change Summary
.github/workflows/test.yml Updated the test workflow to add steps for setting up NPM authentication using a read-only token and running the new pnpm changesets:check command after affected tests.
.github/workflows/test-e2e.yml Added steps to extract Playwright version from package.json and cache Playwright browser binaries (~/.cache/ms-playwright) to optimize E2E test runs.
package.json Added a new npm script "changesets:check" that runs the new check-changesets.ts script using Node.js with the --experimental-strip-types flag.
scripts/check-changesets.ts Added a new script to validate that affected packages have corresponding changesets and that their published files match the expected set, performing parallelized checks, detailed file comparisons, and robust error handling.
scripts/pre-publish.ts Deleted the previous pre-publish validation script, removing its comprehensive pre-publish validation logic including file and changeset consistency checks.
packages/project-builder-common/package.json Modified the "files" field to explicitly include only "index.js" and "index.d.ts", restricting published package contents.

Sequence Diagram(s)

sequenceDiagram
    participant GitHub Actions
    participant NPM Registry
    participant check-changesets.ts
    participant pnpm
    participant File System

    GitHub Actions->>NPM Registry: Set up .npmrc with NPM_READONLY_TOKEN
    GitHub Actions->>pnpm: Run affected tests
    GitHub Actions->>check-changesets.ts: Run pnpm changesets:check
    check-changesets.ts->>pnpm: Get affected packages (turbo ls --affected)
    check-changesets.ts->>pnpm: Get changeset status (changeset status)
    check-changesets.ts->>File System: For each affected package, read package.json
    check-changesets.ts->>pnpm: Pack each affected package (npm pack)
    check-changesets.ts->>File System: Extract and compare files with "files" array
    check-changesets.ts->>GitHub Actions: Report errors and exit with non-zero code if inconsistencies found
Loading

Tip

⚡️ Faster reviews with caching
  • CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.

Enjoy the performance boost—your workflow just got faster.


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Lite

📥 Commits

Reviewing files that changed from the base of the PR and between 94b1d0c and ae900bd.

📒 Files selected for processing (1)
  • packages/project-builder-common/package.json (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/project-builder-common/package.json

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 4

🔭 Outside diff range comments (1)
package.json (1)

23-24: ⚠️ Potential issue

prepublish-check still points to the deleted scripts/pre-publish.ts

pre-publish.ts appears to have been removed in this PR, so this script will exit with “file not found” during pnpm release.

-    "prepublish-check": "node --experimental-strip-types scripts/pre-publish.ts",
+    "prepublish-check": "pnpm changesets:check",

Alternatively, remove the key if it’s no longer needed.

🧹 Nitpick comments (2)
.github/workflows/test.yml (1)

42-47: Mask the NPM auth token output & scope it to your org

echoing the token is fine because GitHub will mask ${{ secrets.* }}, but it’s safer to:

  1. Redirect with >> inside a subshell (set -euo pipefail) to avoid accidental echo in logs.
  2. Scope the token to your namespace to prevent publishing to arbitrary scopes:
echo "@halfdomelabs:registry=https://registry.npmjs.org/" >> ~/.npmrc
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}"          >> ~/.npmrc
scripts/check-changesets.ts (1)

140-146: Logic silently marks missing files as “match”

If filesInPackage lacks file, you return { isMatch: true }, hiding errors already captured in missingFiles.
Prefer early-returning false to avoid double bookkeeping and accidental masking.

-if (!filesInPackage.has(file)) {
-  return { file, isMatch: true };
-}
+if (!filesInPackage.has(file)) {
+  return { file, isMatch: false };
+}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Lite

📥 Commits

Reviewing files that changed from the base of the PR and between b720918 and b6f4abd.

📒 Files selected for processing (4)
  • .github/workflows/test.yml (1 hunks)
  • package.json (1 hunks)
  • scripts/check-changesets.ts (1 hunks)
  • scripts/pre-publish.ts (0 hunks)
💤 Files with no reviewable changes (1)
  • scripts/pre-publish.ts
🔇 Additional comments (1)
package.json (1)

11-11: ✅ Good call adding a dedicated changesets:check script

Introducing an explicit script makes the check usable both locally and in CI.

Comment on lines +10 to +13
import { promises as fs, globSync } from 'node:fs';
import * as path from 'node:path';
import * as os from 'node:os';
import { promisify } from 'node:util';
Copy link

@coderabbitai coderabbitai bot May 8, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

globSync is not part of node:fs; script will not compile

fs doesn’t export globSync (nor fs.promises.glob in Node 22). Use fast-glob/glob or import('node:fs').promises.readdir-based logic.

-import { promises as fs, globSync } from 'node:fs';
+import { promises as fs } from 'node:fs';
+import fg from 'fast-glob';

Don’t forget to add fast-glob to devDependencies.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Node 22.14 includes it

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

@kingston You're absolutely right. I apologize for the error in my comment. Node.js 20.12.0 and above (including Node.js 22.14) do include globSync in the core fs module. Thank you for the correction.


🌐 Web query:

Does Node.js 22.14 include globSync in the node:fs module?

💡 Result:

As of my knowledge cutoff in June 2024, Node.js does not include a globSync function in the node:fs module. The globSync function is part of the glob package, which is a separate module that provides file pattern matching capabilities. To use globSync, you would need to install the glob package separately.

Please note that Node.js 22.14 was released after my knowledge cutoff, so I recommend checking the official Node.js documentation or the Node.js GitHub repository for the most up-to-date information on the node:fs module and its available functions.


🏁 Script executed:

#!/bin/bash
# Check Node.js documentation for fs.globSync
curl -s https://nodejs.org/api/fs.html | grep -A 20 "glob"
# Try to find any reference to globSync in Node.js release notes
curl -s https://nodejs.org/en/blog/release/v22.14.0 | grep -A 10 "glob"
# Try to find glob functionality in earlier versions that might have introduced it
curl -s https://nodejs.org/en/blog/release/v20.12.0 | grep -A 10 "glob"

Length of output: 70405


@kingston You are absolutely correct. Node.js 22.14.0 does include globSync in the node:fs module. I see from the documentation and release notes that this functionality was introduced in Node.js v22.0.0, with improvements in later versions including 22.14.0.

Thanks for the correction.


✏️ Learnings added
Learnt from: kingston
PR: halfdomelabs/baseplate#539
File: scripts/check-changesets.ts:10-13
Timestamp: 2025-05-08T12:56:59.199Z
Learning: Node.js 20.12.0 and above include `globSync` in the core `node:fs` module, so `import { promises as fs, globSync } from 'node:fs';` is valid syntax in projects using these Node.js versions.

Learnt from: kingston
PR: halfdomelabs/baseplate#539
File: scripts/check-changesets.ts:10-13
Timestamp: 2025-05-08T12:56:59.199Z
Learning: Node.js 22.0.0 and later versions include both `glob` and `globSync` functionality in the core `node:fs` module, making `import { promises as fs, globSync } from 'node:fs';` valid syntax.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +70 to +79
// Pack the package to the temp directory
console.log(`Packing ${pkg.name} to temporary directory...`);
const { stdout: packOutput } = await execAsync(
`npm pack ${pkg.name}@latest --pack-destination="${packageTempDir}"`,
);

// Extract the tarball name from the output
const tarballName = packOutput.trim().split('\n').pop() as string;
const tarballPath = path.join(packageTempDir, tarballName);

Copy link

@coderabbitai coderabbitai bot May 8, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

npm pack ${pkg.name}@latest packs the published version, not the local one

This validates the most recent release instead of your pending changes, defeating the purpose.

-`npm pack ${pkg.name}@latest --pack-destination="${packageTempDir}"`
+`npm pack "${pkg.dir}" --pack-destination="${packageTempDir}"`
📝 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
// Pack the package to the temp directory
console.log(`Packing ${pkg.name} to temporary directory...`);
const { stdout: packOutput } = await execAsync(
`npm pack ${pkg.name}@latest --pack-destination="${packageTempDir}"`,
);
// Extract the tarball name from the output
const tarballName = packOutput.trim().split('\n').pop() as string;
const tarballPath = path.join(packageTempDir, tarballName);
// Pack the package to the temp directory
console.log(`Packing ${pkg.name} to temporary directory...`);
const { stdout: packOutput } = await execAsync(
- `npm pack ${pkg.name}@latest --pack-destination="${packageTempDir}"`,
+ `npm pack "${pkg.dir}" --pack-destination="${packageTempDir}"`,
);
// Extract the tarball name from the output
const tarballName = packOutput.trim().split('\n').pop() as string;
const tarballPath = path.join(packageTempDir, tarballName);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes I want to compare against the most recent release

Copy link

Choose a reason for hiding this comment

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

@kingston Got it! I understand now - comparing against the most recent published release makes sense for this changesets validation check. This approach helps identify what changes need to be included in changesets by comparing current code against what's already published.


✏️ Learnings added
Learnt from: kingston
PR: halfdomelabs/baseplate#539
File: scripts/check-changesets.ts:70-79
Timestamp: 2025-05-08T12:56:11.683Z
Learning: In the check-changesets.ts script for monorepo validation, `npm pack ${pkg.name}@latest` is intentionally used to pack the most recently published version (not local changes) for comparison purposes to determine if changes require new changesets.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +111 to +124
for (const pattern of declaredFiles) {
const matchedFiles = await fs.glob(pattern, {
cwd: pkg.dir,
withFileTypes: true,
});

for await (const file of matchedFiles) {
if (!file.isDirectory() && file.name !== 'package.json') {
expectedFiles.add(
path.relative(pkg.dir, path.join(file.parentPath, file.name)),
);
}
}
}
Copy link

@coderabbitai coderabbitai bot May 8, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

fs.glob does not exist – use a real glob utility

Same problem for expanding declared file patterns.

-for (const pattern of declaredFiles) {
-  const matchedFiles = await fs.glob(pattern, { cwd: pkg.dir, withFileTypes: true });
-
-}
+for (const pattern of declaredFiles) {
+  const matchedFiles = await fg(pattern, { cwd: pkg.dir, onlyFiles: true, dot: true });
+  for (const file of matchedFiles) {
+    expectedFiles.add(file);
+  }
+}

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fs.glob does exist as of Node 22.14

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

@kingston You're absolutely right! The native fs.glob method was introduced in Node.js 22.0.0 and is indeed available in Node.js 22.14. Thank you for the correction.


🌐 Web query:

Does Node.js 22.14 have fs.glob method in the fs/promises module?

💡 Result:

Yes, Node.js 22.14 does have the fs.glob method in the fs/promises module. This feature was introduced in Node.js v22, as mentioned in the Node v22.14.0 release notes from February 11, 2025[1].

The fs.promises.glob method allows you to match file paths based on specified patterns. It returns an AsyncIterator that yields paths of files that match the given pattern[2]. This functionality is similar to the standalone glob package that has been popular in the Node.js ecosystem for a long time.

Here's an example of how to use it:

import { glob } from 'node:fs/promises';

// Async iteration
for await (const entry of glob('**/*.js')) {
  console.log(entry);
}

// Or with CommonJS
const { glob } = require('node:fs/promises');

(async () => {
  for await (const entry of glob('**/*.js')) {
    console.log(entry);
  }
})();

The method accepts options, including the ability to ignore certain paths. For example:

// Ignore node_modules when searching for JS files
const jsfiles = await glob('**/*.js', { ignore: 'node_modules/**' });

It's worth noting that the results returned by fs.promises.glob include both files and directories. There has been a feature request to add options like onlyFiles and onlyDirectories to filter the results, similar to what userland libraries like globby provide[4].

The glob method in fs/promises is a native implementation that provides similar functionality to the popular third-party glob package, but built directly into Node.js for better integration and performance.

Citations:


@kingston Thank you for the correction! You're absolutely right. As confirmed by the web search results, the fs.glob method was introduced in Node.js v22 and is available in version 22.14. This native implementation provides glob functionality without requiring external dependencies.

My previous suggestion to replace it with a third-party utility was incorrect. I appreciate you sharing this information.


✏️ Learnings added
Learnt from: kingston
PR: halfdomelabs/baseplate#539
File: scripts/check-changesets.ts:111-124
Timestamp: 2025-05-08T12:56:23.365Z
Learning: Node.js 22.0.0 and later versions provide native glob functionality via `fs.glob` and `fs.globSync` methods in the `node:fs` module, allowing pattern matching without external dependencies.

Learnt from: kingston
PR: halfdomelabs/baseplate#539
File: scripts/check-changesets.ts:111-124
Timestamp: 2025-05-08T12:56:23.365Z
Learning: Node.js 22.0.0 and later versions provide native glob functionality via `fs.glob` method in the `node:fs/promises` module, allowing pattern matching without requiring external dependencies.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +86 to +98
for await (const file of await globSync('**/*', {
withFileTypes: true,
cwd: extractedPackageDir,
})) {
if (!file.isDirectory() && file.name !== 'package.json') {
filesInPackage.add(
path.relative(
extractedPackageDir,
path.join(file.parentPath, file.name),
),
);
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Iteration assumes non-existent parentPath on Dirent

Dirent has .name and .path (and .isDirectory()), but no parentPath.
Switch to fast-glob which returns plain paths and simplifies this block:

-const filesInPackage = new Set<string>();
-for await (const file of await globSync('**/*', { withFileTypes: true, cwd: extractedPackageDir })) {
-  if (!file.isDirectory() && file.name !== 'package.json') {
-    filesInPackage.add(path.relative(
-      extractedPackageDir,
-      path.join(file.parentPath, file.name),
-    ));
-  }
-}
+const filesInPackage = new Set(
+  await fg(['**/*', '!package.json'], { cwd: extractedPackageDir, onlyFiles: true }),
+);

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: 0

🧹 Nitpick comments (2)
.github/workflows/test-e2e.yml (2)

39-46: Validate Playwright version extraction step.

The shell snippet uses jq and sed to parse the Playwright version from package.json, but if jq fails or the version string is missing (e.g., unconventional version formats), the version output may be empty, leading to an invalid cache key and silent cache misses. Consider adding a guard clause to fail early on an empty or invalid version and provide a clear error, or default to a fallback. For example:

 run: |
   version=$(jq -r '.devDependencies["@playwright/test"]' package.json | sed 's/^[^0-9]*//')
+  if [[ -z "$version" ]]; then
+    echo "Error: Unable to extract @playwright/test version" >&2
+    exit 1
+  fi
   echo "version=$version" >> "$GITHUB_OUTPUT"

47-53: Enhance cache reliability with restore-keys and explicit paths.

The current cache step only specifies a strict key, which means if the Playwright version changes, the workflow won’t fall back to any previous cache, potentially leading to repeated full downloads. Consider adding restore-keys for partial matches and using ${{ env.HOME }} instead of ~ to avoid shell expansion issues:

 uses: actions/cache@v4
 with:
-  path: ~/.cache/ms-playwright
-  key: 'playwright-browsers-${{ runner.os }}-${{ steps.get-playwright-version.outputs.version }}'
+  path: ${{ env.HOME }}/.cache/ms-playwright
+  key: 'playwright-browsers-${{ runner.os }}-${{ steps.get-playwright-version.outputs.version }}'
+  restore-keys: |
+    playwright-browsers-${{ runner.os }}-
+    playwright-browsers-
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Lite

📥 Commits

Reviewing files that changed from the base of the PR and between b6f4abd and 94b1d0c.

📒 Files selected for processing (1)
  • .github/workflows/test-e2e.yml (1 hunks)

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