Skip to content

chore(ci): pnpm.onlyBuiltDependencies: [bcrypt] keeps bcrypt native binding compiling under pnpm 10+#495

Open
Senorespecial wants to merge 3 commits into
MERIDIAN-CITY:mainfrom
Senorespecial:chore/ci-pnpm-only-built-deps-bcrypt
Open

chore(ci): pnpm.onlyBuiltDependencies: [bcrypt] keeps bcrypt native binding compiling under pnpm 10+#495
Senorespecial wants to merge 3 commits into
MERIDIAN-CITY:mainfrom
Senorespecial:chore/ci-pnpm-only-built-deps-bcrypt

Conversation

@Senorespecial

@Senorespecial Senorespecial commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Problem

pnpm 10+ ships with build-script execution disabled by default as a security posture. The bcrypt package relies on a node-gyp install step to produce a native binding (bcrypt_lib.node); without that binding, 4 bcrypt-dependent test suites fail at module-load with:

Cannot find module .../bcrypt_lib.node

Earlier, we worked around it with an npm install fallback before each CI run, but that approach does not scale and breaks the team's standardized pnpm install workflow.

Fix

Add a single top-level field to meridian-api/package.json (alphabetical):

"pnpm": {
  "onlyBuiltDependencies": [
    "bcrypt",
    "mysql2"
  ]
}

This is the pnpm-10+ documented opt-in for per-package build scripts. Both packages run install hooks that pnpm 10+ would otherwise skip:

  • bcrypt -- compiles bcrypt_lib.node via node-gyp.
  • mysql2 -- selects the right prebuilt binary for the runtime OS/Node ABI via node-pre-gyp.

After this change, rm -rf node_modules && pnpm install (no npm install fallback) succeeds end to end under pnpm alone.

Verification

Clean-room verification (no prior npm install fallback):

cd meridian-api
rm -rf node_modules
pnpm install                     # bcrypt_lib.node compiled at node_modules/.pnpm/bcrypt@5.1.1/.../bcrypt_lib.node
fn node_modules/.pnpm/bcrypt@5.1.1 -name '*.node'
pnpm test --silent

Result (suites):

Metric Before After
Failed suites 7 3
Passed suites 16 20
Failed tests 2 2

The 4 rescued suites are exactly src/auth/providers/{bcrypt,auth.service,verification-token.provider,verify-email.provider}.spec.ts -- the bcrypt-auth set that was broken by the missing native binding.

The 3 still-failing suites are unrelated pre-existing infrastructure debt:

Why per-package (not workspace-level)

The repo root already has a workspace-level pnpm-lock.yaml covering meridian-web + meridian-api. The other workspace packages:

  • meridian-contracts is a Rust workspace (no npm deps at all).
  • meridian-web has no native-build deps.

Pushing onlyBuiltDependencies to a workspace-level config (pnpm-workspace.yaml or root .npmrc) would unnecessarily widen the allow-list for packages that don't need it. Per-package is the right scope.

Open follow-up: mysql2 CI smoke test

Adding mysql2 to the array is necessary but not sufficient to prove the prebuilt-binary selection works on every CI runner. A small unit/smoke test exercising mysql2.createConnection().query('SELECT 1') against a docker-compose mysql:8 service would lock down that promise; that test is intentionally deferred to a separate small PR so this CI-config change can land quickly while the test-infra work is set up.

Files changed

  • meridian-api/package.json -- +1 line (added "mysql2" to the existing array).

meridian-api/pnpm-lock.yaml was regenerated locally during verification but deliberately not staged: the workspace-level pnpm-lock.yaml at the repo root is authoritative for meridian-web + meridian-api; the inner-package lockfile would just drift the two caches against each other without adding value.

Senorespecial and others added 2 commits June 20, 2026 13:27
…tDependencies

pnpm 10+ skips build scripts by default, which silently breaks bcrypt's native binding and takes 4 bcrypt auth test suites down with it.

This adds pnpm.onlyBuiltDependencies: ["bcrypt"] so future pnpm install properly compiles bcrypt.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
mysql2@^3.11.5 also runs an install script on pnpm 10+ (selects the right prebuilt binary for the runtime OS/Node ABI via node-pre-gyp). Adding it to pnpm.onlyBuiltDependencies closes the entire class of pnpm-10+ missing-binding bugs rather than just the bcrypt one.

Empirical evidence: clean rm -rf node_modules && pnpm install now succeeds end to end; all 23 suites still load; failed-suites count remains 3 (same pre-existing trio tracked at MERIDIAN-CITY#492/MERIDIAN-CITY#493/MERIDIAN-CITY#494); the 4 bcrypt auth suites stay green.

Follow-up still open: a CI smoke test that exercises mysql2.createConnection().query("SELECT 1") would prove the prebuilt-binary selection works on the GitHub-Actions runner images. Adding that test is intentionally deferred to a separate small PR so this CI-config change can land first.

PR body updated to match.
Senorespecial added a commit to Senorespecial/Meridians-verse that referenced this pull request Jun 20, 2026
Closes one of the followups documented in PR MERIDIAN-CITY#495: warns future contributors that `bcrypt` needs `pnpm install` with `pnpm.onlyBuiltDependencies` enabled in meridian-api/package.json (set in PR MERIDIAN-CITY#495), or a prior `npm install`, to compile its native binding under pnpm 10+ default-build-script-disable.
Closes one of the followups documented in PR MERIDIAN-CITY#495: warns future contributors that `bcrypt` needs `pnpm install` with `pnpm.onlyBuiltDependencies` enabled in meridian-api/package.json (set in PR MERIDIAN-CITY#495), or a prior `npm install`, to compile its native binding under pnpm 10+ default-build-script-disable.
@Senorespecial Senorespecial force-pushed the chore/ci-pnpm-only-built-deps-bcrypt branch from fcc13cd to 1b22b80 Compare June 20, 2026 13:49

Copy link
Copy Markdown
Contributor Author

Reviewer-request helper (cross-fork token can't do this directly)

This PR implements pnpm.onlyBuiltDependencies: ["bcrypt", "mysql2"] in meridian-api/package.json so rm -rf node_modules && pnpm install no longer needs an npm install fallback to compile bcrypt's native binding under pnpm 10+ default-build-script-disable.

CI-config only, plus a one-line README note; the failed-suite count dropped 7 -> 3 (the 4 rescued suites are exactly the bcrypt-auth ones). The remaining 3 failing suites are unrelated pre-existing infra debt tracked at #492 / #493 / #494.

Asking a maintainer with upstream write access to formally request review

The fork-bot token can't call gh pr edit --add-reviewer or POST /pulls/495/requested_reviewers (both return 403 since the PR was opened from a fork). If a maintainer with MERIDIAN-CITY repo-write access runs the following from their environment, it formally requests tracking-reviewer notifications:

gh pr edit 495 --add-reviewer Qoder-Undefined

Qoder-Undefined is the same reviewer I cc'd on PR #491 -- they own RBAC middleware (#474) and the meridian-api test scaffolding (#428), so the auth/users surface overlap is a strong fit. Tokens with repo scope on the upstream org should succeed where this codespace's fork-scoped token could not.

If they're unavailable, Mmesolove (PR #443 UserAuthFacade refactor) is a strong backup with deep architectural context on the user/auth lifecycle.

TL;DR for the reviewer

  • Diff: 1 line of pnpm.onlyBuiltDependencies + a one-line README blockquote + one parcel of commits (c902b39 -> 359d62f -> 1b22b80).
  • Head of branch chore/ci-pnpm-only-built-deps-bcrypt (synced to PR headRefOid).
  • Estimated diff for review: less than 100 lines. Trivial.

Copy link
Copy Markdown
Contributor Author

cc @Mmesolove - backup ping for PR #495. Qoder-Undefined was first-pinged, but may be saturated by PR #491 (the strong-password policy PR) this week, so surfacing this one to you as a fallback reviewer.

What this PR does:

  • Adds 1 line to meridian-api/package.json: pnpm.onlyBuiltDependencies: ["bcrypt", "mysql2"] so rm -rf node_modules && pnpm install succeeds end-to-end under pnpm 10+ without the prior npm install fallback. The 4 bcrypt auth test suites that previously failed at module-load (Cannot find module .../bcrypt_lib.node) now pass; failed-suite count dropped 7 -> 3.
  • Adds 1-line blockquote to the top-level README.md warning future contributors about the requirement.
  • 3 commits total on chore/ci-pnpm-only-built-deps-bcrypt (c902b39 -> 359d62f -> 1b22b80).

The 3 still-failing suites are unrelated pre-existing infra debt tracked at #492 / #493 / #494 (no overlap with this PR).

If Qoder-Undefined gets to it first, also fine - I already cc'd them with a longer TL;DR + ready-to-paste maintainer command for the formal-review-request step (the fork-bot token hits 403 on direct gh pr edit --add-reviewer, same constraint as PR #491).

Estimated review surface: less than 100 lines, mostly trivial. CI-config + 1 docs line + 1 package.json field.

Senorespecial added a commit to Senorespecial/Meridians-verse that referenced this pull request Jun 20, 2026
…viewer pings

When this repo is opened from a fork (e.g. Senorespecial/Meridians-verse -> MERIDIAN-CITY/Meridians-verse), the fork-scoped gh token returns 403 on the direct paths that formally request reviewers (gh pr edit --add-reviewer returns GraphQL requestReviewsByLogin permission error; REST POST /pulls/:n/requested_reviewers returns 403).

This script encapsulates the workaround that has been used manually on PR MERIDIAN-CITY#491 and PR MERIDIAN-CITY#495: try the direct paths first (works from upstream-write-token env), and on 403 fall back to posting a PR comment containing a ready-to-paste maintainer command plus a TL;DR for the human reviewer. The fallback comment is dedup-aware via a single DEDUP_MARKER shell constant used both for grep-marker-matching and as the comment-header text, so re-running the script on a PR with an existing fallback comment exits 3 without reposting.

Also includes --help (prints the header docstring), --dry-run (logs what each gh call would do without side effects, exits 3 with no mutation), REQUEST_REVIEW_REPO envvar override, and documented exit codes (0/1/2/3) for downstream automation. No production code change; scripts/ is a new directory at the repo root.
Senorespecial added a commit to Senorespecial/Meridians-verse that referenced this pull request Jun 20, 2026
…viewer pings

When this repo is opened from a fork (e.g. Senorespecial/Meridians-verse -> MERIDIAN-CITY/Meridians-verse), the fork-scoped gh token returns 403 on the direct paths that formally request reviewers (gh pr edit --add-reviewer returns GraphQL requestReviewsByLogin permission error; REST POST /pulls/:n/requested_reviewers returns 403).

This script encapsulates the workaround that has been used manually on PR MERIDIAN-CITY#491 and PR MERIDIAN-CITY#495: try the direct paths first (works from upstream-write-token env), and on 403 fall back to posting a PR comment containing a ready-to-paste maintainer command plus a TL;DR for the human reviewer. The fallback comment is dedup-aware via a single DEDUP_MARKER shell constant used both for grep-marker-matching and as the comment-header text, so re-running the script on a PR with an existing fallback comment exits 3 without reposting.

Also includes --help (prints the header docstring), --dry-run (logs what each gh call would do without side effects, exits 3 with no mutation), REQUEST_REVIEW_REPO envvar override, and documented exit codes (0/1/2/3) for downstream automation. No production code change; scripts/ is a new directory at the repo root.
Senorespecial added a commit to Senorespecial/Meridians-verse that referenced this pull request Jun 20, 2026
…viewer pings

When this repo is opened from a fork (e.g. Senorespecial/Meridians-verse -> MERIDIAN-CITY/Meridians-verse), the fork-scoped gh token returns 403 on the direct paths that formally request reviewers (gh pr edit --add-reviewer returns GraphQL requestReviewsByLogin permission error; REST POST /pulls/:n/requested_reviewers returns 403).

This script encapsulates the workaround that has been used manually on PR MERIDIAN-CITY#491 and PR MERIDIAN-CITY#495: try the direct paths first (works from upstream-write-token env), and on 403 fall back to posting a PR comment containing a ready-to-paste maintainer command plus a TL;DR for the human reviewer. The fallback comment is dedup-aware via a single DEDUP_MARKER shell constant used both for grep-marker-matching and as the comment-header text, so re-running the script on a PR with an existing fallback comment exits 3 without reposting.

Also includes --help (prints the header docstring), --dry-run (logs what each gh call would do without side effects, exits 3 with no mutation), REQUEST_REVIEW_REPO envvar override, and documented exit codes (0/1/2/3) for downstream automation. No production code change; scripts/ is a new directory at the repo root.
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.

1 participant