Skip to content

Commit c3d158f

Browse files
SashaMITcursoragent
andcommitted
fix(dockerfile): rehab pc2-node/Dockerfile for bugs 2-5 caught by docker-smoke
Three surgical fixes to pc2-node/Dockerfile, all targeting bugs that docker-smoke (added 2026-05-18 in CI-HARDENING-A4-D1) caught on its first 2 runs: BUG 2 (BOOT-BLOCKING): Config copy path wrong Was: COPY --from=builder /app/config ./config Now: COPY --from=builder /app/pc2-node/config ./config loader.ts:139 computes DEFAULT_CONFIG_PATH = __dirname/../../config/default.json which resolves to /app/config/default.json at runtime. The previous path was copying from the host repo-root /config/ which only contains pc2.json.example + pc2.production.json — NO default.json. The actual file lives at pc2-node/config/default.json. Container exited with "Default config not found" on every previous run. BUG 3: WASM apps never copied to production image Added: COPY --from=builder /app/pc2-node/wasm-apps ./wasm-apps 7 .wasm binaries (cenc-decrypt, cenc-encrypt, ddrm-renderer, mp4-split, ipfs-assemble, evm-multicall, amm-engine) are committed to the repo as pre-built artifacts but never made it into the production image. Result: dDRM + media segmentation + AMM all silently fall back to inferior paths. We do NOT run build-wasm.sh in Dockerfile — requires Rust+cargo+ wasm-opt (~200MB build-stage bloat) for binaries already committed. BUGS 4+5: sharp + @napi-rs/canvas native bindings missing Added after npm ci: RUN cd pc2-node && npm install --no-save --include=optional \ sharp @napi-rs/canvas Classic npm/cli#4828: pc2-node/package-lock.json was generated on macOS-arm64 (lead dev environment), so it only records @img/sharp-darwin-arm64 and @napi-rs/canvas-darwin-arm64. When npm ci runs on alpine/linux/musl/x64 in the Dockerfile builder, it strictly follows the lockfile and silently skips the linux variants that aren't recorded there. Fix: after npm ci, force a non-lockfile install with --include=optional --no-save which triggers each package's install/check.js hook to detect alpine/linux/musl/x64 and download the correct prebuilts (@img/sharp-linuxmusl-x64 + libvips sibling, @napi-rs/canvas-linux-x64-musl). --no-save means we don't modify package.json or the lockfile. Note: legacy canvas@2.x left as graceful-degradation fallback (already in optionalDependencies, codebase has fallback logic). Migrating to @napi-rs/canvas everywhere is Phase 2 source-code work, separate ticket. VALIDATION: - All 7 .wasm files confirmed tracked in git (git ls-files) - default.json + config.json + models-catalog.json confirmed at pc2-node/config/ (correct location for COPY) - .dockerignore only excludes Dockerfile/.dockerignore/node_modules/puter — wasm-apps + config will be in build context See .cursor/tasks/RELEASE-ENGINEERING-V1280/DOCKERFILE-REHAB-V1280.md for full diagnoses and acceptance criteria. Aims to take docker-smoke from experimental-red to first green run. Need 3 consecutive green runs to promote to required gate. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent dc0537e commit c3d158f

2 files changed

Lines changed: 181 additions & 2 deletions

File tree

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Task: DOCKERFILE-REHAB-V1280 — fix 4 Dockerfile bugs caught by docker-smoke
2+
3+
**Task ID**: `DOCKERFILE-REHAB-V1280`
4+
**Created**: 2026-05-18
5+
**Status**: **InProgress** — executing in same session as CI-HARDENING-A4-D1
6+
**Priority**: Medium (Docker deployment shape only; does not block Mac launcher)
7+
**Predecessor**: `CI-HARDENING-A4-D1` (added the docker-smoke job that surfaced these bugs)
8+
9+
---
10+
11+
## Why this exists
12+
13+
The docker-smoke CI job introduced 2026-05-18 by `CI-HARDENING-A4-D1` caught 4 Dockerfile bugs that would hit every supernode operator running `docker build -t pc2-node . -f pc2-node/Dockerfile && docker run pc2-node` from current `main`. Bug 1 was fixed in `CI-HARDENING-A4-D1` (Go 1.22→1.24). This ticket addresses bugs 2-5.
14+
15+
## Bug catalog with diagnoses
16+
17+
### Bug 2: Config file copy path is wrong (BOOT-BLOCKING)
18+
19+
**Symptom** (from CI log):
20+
```
21+
[ERROR] ❌ Failed to load configuration: Error: Default config not found: /app/config/default.json
22+
at loadDefaultConfig (/app/src/config/loader.ts:147:15)
23+
[PC2] exit code=1
24+
```
25+
26+
**Root cause**:
27+
- `pc2-node/src/config/loader.ts:139` defines `DEFAULT_CONFIG_PATH = join(__dirname, '../../config/default.json')`, so at runtime with `__dirname = /app/src/config`, the loader expects `/app/config/default.json`.
28+
- The Dockerfile line 119 copies from the WRONG location: `COPY --from=builder /app/config ./config` — this is the BUILDER's `/app/config/`, which after `COPY . .` (line 77) is the host's repo-root `/config/` directory. That directory only contains `pc2.json.example` and `pc2.production.json` — NO `default.json`.
29+
- The actual `default.json` file lives at `pc2-node/config/default.json` (host) → `/app/pc2-node/config/default.json` (builder).
30+
31+
**Fix**: Change `COPY --from=builder /app/config ./config``COPY --from=builder /app/pc2-node/config ./config`.
32+
33+
**Severity**: Boot-blocking. This is why every docker-smoke run was failing to reach healthy.
34+
35+
---
36+
37+
### Bug 3: WASM apps not copied to production image
38+
39+
**Symptom** (from CI log):
40+
```
41+
[WARN] [DASHPackager] WASM preload skipped: cenc-encrypt WASM not found
42+
[WARN] [media-api] mp4-split WASM preload skipped
43+
[WARN] [media-api] WASM preload skipped: CENC decrypt WASM not found
44+
```
45+
46+
**Root cause**:
47+
- The 7 `.wasm` binaries ARE committed to git at `pc2-node/wasm-apps/*/*.wasm` (8MB total across cenc-decrypt, cenc-encrypt, ddrm-renderer, mp4-split, ipfs-assemble, evm-multicall, amm-engine).
48+
- The Dockerfile never copies them to the production image. Net result: dDRM features (encrypt/decrypt), media segmentation (mp4-split), and AMM operations all fall back to inferior paths or fail.
49+
50+
**Fix**: Add `COPY --from=builder /app/pc2-node/wasm-apps ./wasm-apps` in the production stage.
51+
52+
**Severity**: Functional degradation (warnings + fallback paths used). Not boot-blocking — server still starts.
53+
54+
**Note**: We do NOT run `pc2-node/scripts/build-wasm.sh` in the Dockerfile because that requires Rust toolchain + cargo + wasm-opt (binaryen) which would add ~200MB to the build stage. Since `.wasm` files are committed to the repo as pre-built artifacts, copying is the right pattern.
55+
56+
---
57+
58+
### Bug 4: `sharp` native binding not found in production stage
59+
60+
**Symptom** (from CI log):
61+
```
62+
[ERROR] [Thumbnail] ❌ Sharp failed to load - image thumbnails will be disabled
63+
[ERROR] [Thumbnail] This is a required dependency. Please reinstall: npm install
64+
```
65+
66+
**Root cause**: classic `npm/cli#4828`. The `pc2-node/package-lock.json` was generated on macOS-arm64 (Sasha's dev machine), so it ONLY contains:
67+
- `@img/sharp-darwin-arm64`
68+
- `@img/sharp-libvips-darwin-arm64`
69+
70+
NO linux/musl variants are recorded in the lockfile. When `npm ci` runs on alpine/linux in the Dockerfile builder stage, it strictly follows the lockfile and silently skips the linux variants. Sharp's runtime probe then fails to find a native binding for the current platform.
71+
72+
This is a well-known npm bug: https://github.com/npm/cli/issues/4828
73+
74+
**Fix**: After `npm ci`, force-install platform-specific variants with `--include=optional`:
75+
```dockerfile
76+
RUN cd pc2-node && npm install --no-save --include=optional sharp
77+
```
78+
79+
`--no-save` means don't modify `package.json` or `package-lock.json`. The install runs sharp's `install/check.js` post-install hook which detects the current platform (alpine/linux/musl/x64) and downloads the right `@img/sharp-linuxmusl-x64` + `@img/sharp-libvips-linuxmusl-x64` prebuilt binaries.
80+
81+
**Severity**: Functional degradation (image thumbnails disabled). Not boot-blocking — fallback paths exist.
82+
83+
---
84+
85+
### Bug 5: `@napi-rs/canvas` native binding not found
86+
87+
**Symptom** (from CI log):
88+
```
89+
Warning: Cannot load "@napi-rs/canvas" package: "Error: Cannot find native binding..."
90+
Warning: Cannot polyfill `DOMMatrix`, rendering may be broken.
91+
[WARN] [Thumbnail] ⚠️ Canvas not available - PDF/text thumbnails will be disabled
92+
```
93+
94+
**Root cause**: Same `npm/cli#4828` bug as #4. Lockfile only has `@napi-rs/canvas-darwin-arm64`, no `@napi-rs/canvas-linux-x64-musl` variant.
95+
96+
**Fix**: Same pattern — force-install with `--include=optional`:
97+
```dockerfile
98+
RUN cd pc2-node && npm install --no-save --include=optional @napi-rs/canvas
99+
```
100+
101+
**Severity**: Functional degradation (PDF/text thumbnails disabled). Not boot-blocking.
102+
103+
**Note**: Legacy `canvas@2.x` is also a fallback target listed in `optionalDependencies`. It needs Cairo/Pango system deps for alpine build-from-source, which would add ~50MB to the image. Since `@napi-rs/canvas` (prebuilt-only) supersedes it, we DO NOT add Cairo/Pango. The Thumbnail service already has graceful degradation when neither canvas variant loads — log shows "PDF.js loaded ✅" as primary path.
104+
105+
---
106+
107+
## Implementation plan
108+
109+
Three Dockerfile changes, all in `pc2-node/Dockerfile`:
110+
111+
1. **After `RUN cd pc2-node && npm ci --include=dev`** (around line 74): Add new RUN step that force-installs sharp + @napi-rs/canvas with `--include=optional` to fix bugs 4 + 5 (npm/cli#4828 workaround).
112+
113+
2. **Replace `COPY --from=builder /app/config ./config`** (line 119) with `COPY --from=builder /app/pc2-node/config ./config` to fix bug 2.
114+
115+
3. **Add new COPY** for `wasm-apps` to fix bug 3.
116+
117+
All changes are inline-documented with the specific bug class and CI evidence.
118+
119+
## Acceptance criteria
120+
121+
- [ ] `pc2-node/Dockerfile` modified per implementation plan
122+
- [ ] CI docker-smoke job reaches `healthy` HEALTHCHECK state
123+
- [ ] No `[ERROR]` lines in the container boot log (warnings about legacy `canvas@2.x` are acceptable)
124+
- [ ] `/api/health` responds with `status: "ok"` from outside the container via port mapping
125+
- [ ] Updated `.cursor/tasks/RELEASE-ENGINEERING-V1280/CI-HARDENING-A4-D1.md` to mark bugs 2-5 as RESOLVED with reference to this ticket
126+
127+
## What this is NOT
128+
129+
- NOT promoting docker-smoke to a required gate. That requires 3 consecutive green runs and is tracked separately.
130+
- NOT re-enabling `pc2-node-docker.yml` publish-to-GHCR workflow. That's a deferred decision pending supernode operator audience growth.
131+
- NOT migrating canvas@2.x@napi-rs/canvas in source code. That's Phase 2 work tracked in OPTIMISATION-AND-REFACTORING-2026-05.
132+
133+
## Execution log
134+
135+
(To be filled in during execution.)

pc2-node/Dockerfile

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,29 @@ COPY pc2-node/package*.json ./pc2-node/
8080
# Install dependencies
8181
RUN cd pc2-node && npm ci --include=dev
8282

83+
# npm/cli#4828 workaround: the pc2-node/package-lock.json is typically
84+
# generated on macOS-arm64 (the lead dev environment), so it ONLY records
85+
# the macOS platform variants of native deps:
86+
# - @img/sharp-darwin-arm64 (sharp's native binding)
87+
# - @napi-rs/canvas-darwin-arm64
88+
# When `npm ci` runs in this alpine/linux/musl/x64 builder, it strictly
89+
# follows the lockfile and silently skips the linux/musl variants that
90+
# aren't recorded there. Runtime then logs "Cannot find native binding"
91+
# for both sharp and @napi-rs/canvas.
92+
#
93+
# Fix: after `npm ci`, force-install the platform-specific variants with
94+
# `--include=optional --no-save`. This triggers each package's own
95+
# install/check.js post-install hook which detects alpine/linux/musl/x64
96+
# and downloads the correct prebuilts (@img/sharp-linuxmusl-x64,
97+
# @napi-rs/canvas-linux-x64-musl, plus their libvips siblings). --no-save
98+
# means we DON'T modify package.json or the lockfile — purely a runtime
99+
# fix-up for this Docker build's environment.
100+
#
101+
# Caught 2026-05-18 by docker-smoke CI on first run. See:
102+
# - https://github.com/npm/cli/issues/4828 (the upstream npm bug)
103+
# - .cursor/tasks/RELEASE-ENGINEERING-V1280/DOCKERFILE-REHAB-V1280.md
104+
RUN cd pc2-node && npm install --no-save --include=optional sharp @napi-rs/canvas
105+
83106
# Copy source code
84107
COPY . .
85108

@@ -122,8 +145,29 @@ COPY --from=builder /app/pc2-node/frontend ./frontend
122145
COPY --from=builder /app/pc2-node/package.json ./
123146
COPY --from=builder /app/pc2-node/tsconfig.json ./
124147

125-
# Copy configuration templates
126-
COPY --from=builder /app/config ./config
148+
# Copy configuration templates.
149+
# Bug 2 fix (2026-05-18 docker-smoke): The previous path `/app/config` was
150+
# the host's repo-root /config/ directory which only contains
151+
# pc2.json.example + pc2.production.json — NO default.json. The actual
152+
# default config lives at pc2-node/config/default.json. The runtime
153+
# loader (pc2-node/src/config/loader.ts:139) computes
154+
# DEFAULT_CONFIG_PATH = join(__dirname, '../../config/default.json')
155+
# which resolves to /app/config/default.json at runtime (since
156+
# __dirname = /app/src/config). Copying pc2-node/config to /app/config
157+
# satisfies that path.
158+
COPY --from=builder /app/pc2-node/config ./config
159+
160+
# Copy pre-built WASM apps.
161+
# Bug 3 fix (2026-05-18 docker-smoke): The 7 .wasm binaries (cenc-decrypt,
162+
# cenc-encrypt, ddrm-renderer, mp4-split, ipfs-assemble, evm-multicall,
163+
# amm-engine) are committed to the repo at pc2-node/wasm-apps/ as
164+
# pre-built artifacts. Without this copy, dDRM features, media
165+
# segmentation, and AMM operations all fall back to inferior paths or
166+
# fail outright with "WASM not found — run scripts/build-wasm.sh".
167+
# We do NOT build WASM in the Dockerfile because that requires the Rust
168+
# toolchain + cargo + wasm-opt (binaryen), ~200MB of build-stage bloat
169+
# for artifacts that are already committed.
170+
COPY --from=builder /app/pc2-node/wasm-apps ./wasm-apps
127171

128172
# Copy entrypoint script
129173
COPY --from=builder /app/pc2-node/scripts/docker-entrypoint.sh /app/docker-entrypoint.sh

0 commit comments

Comments
 (0)