Files
msd-core/docs/contributing/bootstrap.md
Tom Boucher 33ffc647e2 feat(117): reproducible npm environment bootstrap + check-env validator (#136)
* test(117): add failing tests for env validator (check-env.sh)

RED phase. Six tests for scripts/check-env.sh — none pass because the
script does not exist yet. Fixtures:
  good/             — engines.node >=22, .nvmrc 26, synced lockfile
  bad-node-version/ — engines.node <14.0.0 (current Node v26 fails)
  missing-lockfile/ — no package-lock.json
  bad-nvmrc/        — .nvmrc says 22, current Node is v26

Tests cover:
  1. Happy path exits 0
  2. engines.node constraint failure exits 1
  3. Missing lockfile exits 1
  4. .nvmrc major mismatch exits 1
  5. --json flag emits {pass: boolean, checks: array}
  6. Integration smoke: exits 0 on live worktree root

Sources:
  npm engines: https://docs.npmjs.com/cli/v10/configuring-npm/package-json#engines
  npm ci docs: https://docs.npmjs.com/cli/v10/commands/npm-ci

Closes #117

* feat(117): add scripts/check-env.sh with Node/npm/lockfile/version-manager checks

GREEN phase. Implements the five-check environment validator:

  1. Node version vs engines.node (semver constraint — >=, >, <=, <, =)
  2. npm version vs engines.npm (skipped if field absent)
  3. package-lock.json presence
  4. Lockfile sync via `npm ci --dry-run` (exits non-zero when drift detected)
  5. Version-manager pin (.nvmrc / .node-version / .tool-versions) vs active Node major

Exit codes: 0 = all green; 1 = at least one failure; 2 = tool error.
Flags: --json (structured report), --help.

All 7 tests pass. shellcheck clean. bash -n syntax check clean.

Sources:
  npm engines:         https://docs.npmjs.com/cli/v10/configuring-npm/package-json#engines
  Reproducible builds: https://reproducible-builds.org/docs/source-tree/
  npm ci docs:         https://docs.npmjs.com/cli/v10/commands/npm-ci

Closes #117

* chore(117): pin Node engines + .nvmrc; add check:env npm script

- Add engines.npm: ">=10.0.0" (npm 10 ships with Node 22, the CI floor).
  Source: https://docs.npmjs.com/cli/v10/configuring-npm/package-json#engines
- Add .nvmrc pinning Node 22 (lowest supported version per CI matrix in
  .github/workflows/test.yml; node-version: [22, 24]).
- Add "check:env": "./scripts/check-env.sh" script to package.json.

No generator is involved (not a .generated. file). The test update in this
commit adjusts the integration smoke: it now asserts on --json structured
output rather than raw text, and accepts exit 0 or 1 (version-manager pin
mismatch is expected when developer runs Node 26 against a .nvmrc of 22).

Closes #117

* ci(117): wire environment check into test workflow

Add "Environment check" step to .github/workflows/test.yml in the `test`
job. Positioned AFTER actions/setup-node and BEFORE npm ci so that env
mismatches (wrong Node version, missing npm version, absent lockfile) are
caught before the install step obscures the root cause.

Runs `npm run check:env` (./scripts/check-env.sh) on every matrix lane
(ubuntu, macos, windows) × (Node 22, 24).

Source: https://docs.npmjs.com/cli/v10/configuring-npm/package-json#engines

Closes #117

* docs(117): publish docs/contributing/bootstrap.md + link from CONTRIBUTING.md

Adds docs/contributing/bootstrap.md with:
  1. Prerequisites (nvm, fnm, asdf, mise; gh CLI)
  2. One-time setup (clone, nvm use, check:env, npm ci)
  3. Daily commands table
  4. Validation guide (check table, exit codes, --json usage)
  5. Troubleshooting (node-version, npm-version, lockfile-present,
     lockfile-sync, version-manager-pin, missing modules, locale errors)
  6. Alternative: Docker via gsd-test-runner
     (https://github.com/open-gsd/gsd-test-runner)

Adds "Bootstrap your environment" section to CONTRIBUTING.md pointing to
the new doc. No content duplication — CONTRIBUTING.md links only.

Adds .changeset/117-npm-bootstrap.md (type: Added) for changelog.

Sources:
  npm engines:         https://docs.npmjs.com/cli/v10/configuring-npm/package-json#engines
  Reproducible builds: https://reproducible-builds.org/docs/source-tree/
  npm ci docs:         https://docs.npmjs.com/cli/v10/commands/npm-ci
  gsd-test-runner:     https://github.com/open-gsd/gsd-test-runner

Closes #117

* fix(#117): make check:env script run on Windows runners

Invoke check-env.sh via `bash` instead of a bare POSIX path so
Windows CI runners (which have Git Bash on PATH) execute the script
without requiring a POSIX shell shebang dispatcher.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(117): make check-env.sh fixture .nvmrc adapt to active Node major (cross-platform fix)

Before() hook writes good/.nvmrc = activeNodeMajor and bad-nvmrc/.nvmrc = activeNodeMajor+99
at test-run time. Hardcoded .nvmrc=26 failed on every CI matrix row except Node 26.
After() restores originals so the checked-in files stay stable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(117): exempt gsd-test-runner URL path from slash-command registry check

docs/contributing/bootstrap.md links to https://github.com/open-gsd/gsd-test-runner.
The parity-test regex captures /gsd-test-runner from the URL path component and
flags it as an unregistered slash command. Add 'test-runner' to INTERNAL_COMPONENT_SLUGS
(mirrors the existing 'build' entry for GitHub org URLs) with an explanatory comment.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(117): fix Node-24 and Windows-22 CI failures in check-env

Two root causes:

1. version-manager-pin on Node 24 (mac/ubuntu/win):
   The project root .nvmrc pins major 22 for local dev. When the CI
   matrix runs Node 24, check-env.sh fails the version-manager-pin
   check and exits 1, blocking the entire test job before any test
   runs. Fix: skip the pin check when CI=true (GitHub Actions always
   sets this). The pin is a local dev guard, not a gate for multi-
   version matrix CI.

2. engines.node appears missing on Windows-22 (pkg_field backslash):
   pkg_field() embedded PACKAGE_JSON directly into a node -e string
   literal using require(). On Windows, the path uses backslashes
   (D:\a\...) which are silently interpreted as JS escape sequences
   inside the string, causing require() to fail silently (2>/dev/null
   || true). engines.node returns empty, triggering a spurious FAIL.
   Fix: switch to fs.readFileSync + JSON.parse and normalise
   backslashes to forward-slashes before embedding in the JS literal.

Also pass { CI: '' } from the bad-nvmrc unit test so the
version-manager-pin fixture test still exercises the mismatch path
even when running inside CI runners.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(117): use relative ./package.json path in pkg_field to fix Windows CI

On Windows, Git Bash exposes \$PWD as a POSIX path (/d/a/…) which
node.exe cannot resolve via fs.readFileSync. The previous fix embedded
the absolute PACKAGE_JSON path in the node -e string after converting
backslashes to forward-slashes, but the POSIX form produced by Git Bash
(/d/a/…) has no backslashes — so the conversion was a no-op and node
received an unresolvable path. The silent catch(e) { process.exit(0) }
swallowed the ENOENT, returning empty string for every engines.* field.

Fix: use './package.json' (relative to CWD). pkg_field() is always
called before any cd in the script so CWD === PROJECT_ROOT at call time.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 18:38:49 -04:00

6.0 KiB

Bootstrap your environment

This guide gets a new contributor from a fresh checkout to a passing baseline in one session.

Sources:


Prerequisites

Node version manager (pick one)

Tool Install Docs
nvm (recommended) curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/HEAD/install.sh | bash https://github.com/nvm-sh/nvm
fnm (fast, Rust) curl -fsSL https://fnm.vercel.app/install | bash https://github.com/Schniz/fnm
asdf brew install asdf then asdf plugin add nodejs https://asdf-vm.com
mise curl https://mise.run | sh https://mise.jdx.dev

A version manager ensures you can switch Node versions per project without polluting your global install. This project ships a .nvmrc file at the root — any of the tools above will read it.

Other required tools

  • gh (GitHub CLI) — https://cli.github.com — used by contribution workflows and CI
  • git — any recent version

One-time setup

# 1. Clone
git clone https://github.com/open-gsd/get-shit-done-redux.git
cd get-shit-done-redux

# 2. Activate the pinned Node version
nvm use          # nvm
# fnm use        # fnm
# asdf install   # asdf / mise

# 3. Verify the environment (see Validation below)
npm run check:env

# 4. Install dependencies (reproducible, lockfile-driven)
npm ci

npm ci is required over npm install. It installs exactly what package-lock.json specifies and fails fast if the lockfile is out of sync — this is intentional. See https://docs.npmjs.com/cli/v10/commands/npm-ci


Daily commands

Command Purpose
npm run check:env Validate your environment before running tests
npm test Run the full test suite (unit + integration + security)
npm run test:unit Unit tests only (fastest)
npm run test:integration Integration tests
npm run build:sdk Rebuild the SDK dist (required before first test run)

npm run check:integrity — available once #114 merges.


Validation

Run the environment validator before any test or audit run:

npm run check:env

This runs scripts/check-env.sh and reports pass/fail for each check:

Check What it verifies
node-version Active Node satisfies engines.node (>=22.0.0)
npm-version Active npm satisfies engines.npm (>=10.0.0)
lockfile-present package-lock.json exists at root
lockfile-sync npm ci --dry-run exits 0 (lockfile matches installed state)
version-manager-pin Active Node major matches .nvmrc / .node-version / .tool-versions

Exit codes:

  • 0 — all checks passed, safe to proceed
  • 1 — one or more checks failed — see the report
  • 2 — tool error (e.g., node not found, corrupt package.json)

For structured output (useful in scripts):

npm run check:env -- --json

Troubleshooting

node-version FAIL — Node X does NOT satisfy >=22.0.0

Cause: The system Node is too old, or the version manager hasn't activated the correct version.

Fix:

nvm use          # activates version from .nvmrc
node --version   # confirm

If nvm reports the version is not installed:

nvm install      # installs the version in .nvmrc
nvm use

npm-version FAIL — npm X does NOT satisfy >=10.0.0

Cause: npm bundled with an old Node version.

Fix:

npm install -g npm@latest
npm --version

lockfile-present FAIL — package-lock.json missing

Cause: The lockfile was deleted or was never generated.

Fix:

npm install      # generates package-lock.json

Do NOT commit a regenerated lockfile without verifying no unexpected packages changed. Run git diff package-lock.json to inspect the diff.


lockfile-sync FAIL — package-lock.json is out of sync

Cause: package.json was edited (dependency added/changed) without updating the lockfile, or the lockfile was hand-edited.

Fix:

npm ci           # restores node_modules to match lockfile exactly
# or, if the sync failure is intentional (you updated package.json):
npm install      # updates lockfile to match package.json

version-manager-pin FAIL — Active Node major does not match .nvmrc

Cause: The shell is using a globally-installed Node rather than the version manager's activation. Common on fresh shell sessions.

Fix:

nvm use          # re-activate from .nvmrc
# or add to your shell profile:
# echo 'nvm use --silent' >> ~/.zshrc

Tests fail with Error: Cannot find module ...

Cause: node_modules is missing or stale (common after a branch switch that changed package.json).

Fix:

npm ci           # clean install from lockfile
npm run build:sdk

Locale / encoding errors on non-UTF-8 systems

Cause: Some test fixtures contain non-ASCII characters. Node requires a UTF-8 locale.

Fix:

export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

Add these to your shell profile to make them permanent.


Alternative: Docker via gsd-test-runner

For canonical Linux verification from a macOS dev box, use gsd-test-runner.

This is the same path CI uses for cross-platform coverage. It is the authoritative way to confirm your change passes on Linux before opening a PR:

# One-time: install gsd-test-runner (see repo README)
# Then, from the project root:
gsd-test-summary

gsd-test-summary runs the full suite in a Docker container and emits a concise Mac: N failed / Docker: N failed summary. Both lines must show 0 failed before a PR is opened. See CLAUDE.md for the required PR-flow ordering.