Commit package-lock.json files for reproducible builds #19

Closed
opened 2026-07-17 01:23:51 +00:00 by claude-bot · 2 comments
Contributor

The repo has no lockfiles - no package-lock.json at the root, none in backend/, and no yarn/pnpm equivalent. Verified 2026-07-16.

This has three consequences that surfaced while configuring Renovate and CI:

1. Builds are not reproducible. Every dependency uses a caret range (^18.3.1, ^6.0.5, ...). npm install resolves the newest matching version at install time, so CI, your dev server, and the Docker image can each resolve different dependency trees from identical source. A transitive dependency breaking upstream reaches production without any commit here.

2. CI cannot use npm ci. .forgejo/workflows/ci.yml uses npm install because npm ci requires a committed lockfile and fails without one. npm ci is both faster and deterministic.

3. Renovate is substantially weakened. Two specific effects:

  • lockFileMaintenance in renovate.json is currently a no-op with nothing to maintain.
  • In-range minor/patch PRs are close to meaningless. npm install already pulls the latest in-range version, so a PR bumping ^18.3.1 to ^18.3.2 changes almost nothing real. Renovate's value here is mostly limited to majors until lockfiles exist.

Fix: run npm install in both the root and backend/, commit both package-lock.json files, then switch the CI workflow to npm ci. Renovate will pick up lockfile maintenance automatically.

Worth doing before enabling any auto-merge - auto-merging dependency updates that CI cannot deterministically verify is the risky combination.

The repo has **no lockfiles** - no `package-lock.json` at the root, none in `backend/`, and no yarn/pnpm equivalent. Verified 2026-07-16. This has three consequences that surfaced while configuring Renovate and CI: **1. Builds are not reproducible.** Every dependency uses a caret range (`^18.3.1`, `^6.0.5`, ...). `npm install` resolves the newest matching version at install time, so CI, your dev server, and the Docker image can each resolve different dependency trees from identical source. A transitive dependency breaking upstream reaches production without any commit here. **2. CI cannot use `npm ci`.** `.forgejo/workflows/ci.yml` uses `npm install` because `npm ci` requires a committed lockfile and fails without one. `npm ci` is both faster and deterministic. **3. Renovate is substantially weakened.** Two specific effects: - `lockFileMaintenance` in `renovate.json` is currently a no-op with nothing to maintain. - In-range minor/patch PRs are close to meaningless. `npm install` already pulls the latest in-range version, so a PR bumping `^18.3.1` to `^18.3.2` changes almost nothing real. Renovate's value here is mostly limited to majors until lockfiles exist. **Fix:** run `npm install` in both the root and `backend/`, commit both `package-lock.json` files, then switch the CI workflow to `npm ci`. Renovate will pick up lockfile maintenance automatically. Worth doing before enabling any auto-merge - auto-merging dependency updates that CI cannot deterministically verify is the risky combination.
Author
Contributor

Fixed in PR #20.

Both lockfiles generated with npm install --package-lock-only in node:20-alpine (matching the version both Dockerfiles pin):

  • package-lock.json - lockfileVersion 3, 224 packages
  • backend/package-lock.json - lockfileVersion 3, 91 packages

Platform portability checked, since generating on Alpine/musl could in principle miss glibc binaries. It doesn't: rollup declares every platform binary as an optionalDependency, so the lockfile records all variants (rollup-linux-x64-musl, rollup-linux-x64-gnu, rollup-win32-x64-msvc, rollup-darwin-arm64, and the rest) with their os/cpu/libc constraints. npm ci selects the matching one per platform.

Verified end-to-end by running the full CI pipeline in node:20-alpine: npm ci succeeded for both packages, 46 frontend tests passed, 22 backend tests passed, production build succeeded. .forgejo/workflows/ci.yml now uses npm ci rather than npm install.

Also added a .gitignore - the repo had none, which was a live hazard once npm ci starts creating node_modules/. Confirmed it ignores .env while keeping .env.example tracked.

With lockfiles in place, lockFileMaintenance in renovate.json becomes active and in-range minor/patch PRs now represent real changes.

Fixed in PR #20. Both lockfiles generated with `npm install --package-lock-only` in `node:20-alpine` (matching the version both Dockerfiles pin): - `package-lock.json` - lockfileVersion 3, 224 packages - `backend/package-lock.json` - lockfileVersion 3, 91 packages **Platform portability checked**, since generating on Alpine/musl could in principle miss glibc binaries. It doesn't: rollup declares every platform binary as an `optionalDependency`, so the lockfile records all variants (`rollup-linux-x64-musl`, `rollup-linux-x64-gnu`, `rollup-win32-x64-msvc`, `rollup-darwin-arm64`, and the rest) with their `os`/`cpu`/`libc` constraints. `npm ci` selects the matching one per platform. **Verified end-to-end** by running the full CI pipeline in `node:20-alpine`: `npm ci` succeeded for both packages, 46 frontend tests passed, 22 backend tests passed, production build succeeded. `.forgejo/workflows/ci.yml` now uses `npm ci` rather than `npm install`. Also added a `.gitignore` - the repo had none, which was a live hazard once `npm ci` starts creating `node_modules/`. Confirmed it ignores `.env` while keeping `.env.example` tracked. With lockfiles in place, `lockFileMaintenance` in `renovate.json` becomes active and in-range minor/patch PRs now represent real changes.
Author
Contributor

Done in #20, merged as a0d604b. Both lockfiles committed (lockfileVersion 3; 224 and 91 packages), generated in node:20-alpine to match both Dockerfiles. All @rollup/rollup-* platform variants recorded, so npm ci resolves on any host. Added a .gitignore too — the repo had none.

CI uses npm ci with setup-node caching keyed on both lockfiles, and has since gone green on two real runs.

Done in #20, merged as `a0d604b`. Both lockfiles committed (lockfileVersion 3; 224 and 91 packages), generated in `node:20-alpine` to match both Dockerfiles. All `@rollup/rollup-*` platform variants recorded, so `npm ci` resolves on any host. Added a `.gitignore` too — the repo had none. CI uses `npm ci` with `setup-node` caching keyed on both lockfiles, and has since gone green on two real runs.
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/BatteryStorageCalculator#19
No description provided.