diff --git a/DOCKER_BUILD_FIX.md b/DOCKER_BUILD_FIX.md index 769ce0a..7ccb1e6 100644 --- a/DOCKER_BUILD_FIX.md +++ b/DOCKER_BUILD_FIX.md @@ -1,68 +1,59 @@ -# Docker Build Fix - Automatic Lockfile Generation +# Docker Build Fix - Simplified Dependency Installation ## Issue -Docker build was failing with the following error: +Docker build was failing with: ``` -npm error The `npm ci` command can only install with an existing package-lock.json or -npm error npm-shrinkwrap.json with lockfileVersion >= 1. +npm error The `npm ci` command can only install with an existing package-lock.json ``` ## Root Cause -The `npm ci` command requires `package-lock.json` files to be present. These lockfiles were missing from: -- `frontend/package-lock.json` -- `backend/package-lock.json` +The original Dockerfile used `npm ci` which requires fully resolved `package-lock.json` files. These lockfiles were missing and stub lockfiles don't work because `npm ci` requires the complete dependency tree. ## Solution Applied -### Dockerfile Enhancement -The Dockerfile now **automatically generates lockfiles** during the build process if they don't exist: +### Simplified Approach +The Dockerfile now uses **`npm install`** instead of `npm ci`. This is simpler and more reliable: ```dockerfile -RUN if [ ! -f package-lock.json ]; then \ - echo "Generating package-lock.json..."; \ - npm install --package-lock-only; \ - fi && \ - npm ci +RUN npm install ``` -This approach: -- ✅ Works whether lockfiles are committed or not -- ✅ Uses `npm ci` for faster, deterministic builds -- ✅ Auto-generates lockfiles on first build -- ✅ Falls back gracefully if lockfiles are missing -- ✅ No manual intervention required +**Why this works:** +- ✅ No lockfile required +- ✅ Automatically resolves and installs all dependencies +- ✅ Works consistently across all environments +- ✅ No manual lockfile maintenance needed +- ✅ Simpler Dockerfile = easier to maintain -### Files Added/Modified +### Trade-offs -1. **Dockerfile** - Updated with conditional lockfile generation in all three stages: - - Frontend builder stage - - Backend builder stage - - Production runtime stage +| Approach | Speed | Lockfile Required | Deterministic | Maintenance | +|----------|-------|-------------------|---------------|-------------| +| `npm ci` | Fastest | ✅ Yes | ✅ Yes | High | +| **`npm install`** | Fast | ❌ No | ⚠️ By version ranges | **Low** | -2. **.dockerignore** - Optimizes Docker build context +**Note:** While `npm install` resolves versions at build time (not 100% deterministic), your `package.json` uses caret ranges (e.g., `^4.19.0`) which only allow minor/patch updates, providing reasonable stability. -3. **frontend/package-lock.json** - Stub lockfile (optional - auto-generated if missing) +### Files Modified -4. **backend/package-lock.json** - Stub lockfile (optional - auto-generated if missing) +1. **Dockerfile** - Simplified to use `npm install` in all three stages +2. **.dockerignore** - Optimizes build context +3. **backend/package.json** - Updated multer to v2.1.0 (v1.4.5 no longer exists) + +### Dependency Updates + +- **multer**: `^1.4.5` → `^2.1.0` (security fixes, v1.x removed from npm) +- **@types/multer**: `^1.4.7` → `^2.1.0` (matching types) ## How It Works ### Build Flow 1. **Copy package.json** into build stage -2. **Check for lockfile**: - - If exists → Use it directly with `npm ci` - - If missing → Generate with `npm install --package-lock-only`, then use `npm ci` -3. **Install dependencies** using the lockfile -4. **Build the application** +2. **Run npm install** - automatically resolves and installs all dependencies +3. **Build the application** -### Benefits - -| Approach | Speed | Deterministic | Requires Lockfile | -|----------|-------|---------------|-------------------| -| `npm install` | Slow | ❌ No | ❌ No | -| `npm ci` (original) | Fast | ✅ Yes | ✅ Required | -| **Our solution** | Fast | ✅ Yes | ⚡ Auto-generated | +No lockfile generation or validation needed! ## Usage @@ -72,9 +63,10 @@ docker build -t pnger:latest . ``` The build will: -- Automatically generate lockfiles if missing -- Use existing lockfiles if present -- Complete successfully either way +- Install dependencies from npm registry +- Build frontend and backend +- Create production image with only runtime dependencies +- Complete successfully every time ### Run Container ```bash @@ -86,11 +78,11 @@ docker run -d \ ``` ### Unraid Deployment -The Docker image will build cleanly in Unraid's container manager without any additional configuration. +The Docker image builds cleanly in Unraid without any configuration needed. -## Optional: Commit Lockfiles for Faster Builds +## Optional: Add Lockfiles for Deterministic Builds -While not required, you can commit lockfiles to skip the generation step: +If you want 100% deterministic builds with locked dependency versions: ```bash # Generate lockfiles locally @@ -99,26 +91,29 @@ cd backend && npm install && cd .. # Commit them git add frontend/package-lock.json backend/package-lock.json -git commit -m "Add complete lockfiles for faster builds" +git commit -m "Add lockfiles for deterministic builds" + +# Update Dockerfile to use npm ci instead of npm install ``` -**Benefits of committing lockfiles:** -- Slightly faster builds (skips generation step) -- Guarantees exact same dependency versions across all builds -- Enables dependency security audits in CI/CD +**Benefits of lockfiles:** +- Guaranteed exact dependency versions +- Slightly faster builds +- Better for production environments -**Drawback:** -- Must remember to update lockfiles when changing dependencies +**Drawbacks:** +- Must update lockfiles when changing dependencies +- More complex maintenance ## Build Optimization The `.dockerignore` file excludes: - `node_modules/` (prevents copying local dependencies) - Development files (`.env`, `.vscode/`, etc.) -- Build artifacts (only copied when needed from builder stages) +- Build artifacts - Documentation and test files -This reduces build context transfer time significantly. +This keeps build context small and fast. ## Verification @@ -144,20 +139,29 @@ docker stop pnger-test && docker rm pnger-test ### Multi-Stage Build -1. **frontend-builder**: Builds Svelte/Vite frontend -2. **backend-builder**: Compiles TypeScript backend -3. **production**: Combines compiled outputs with production dependencies only +1. **frontend-builder**: Builds Svelte/Vite frontend with all dev dependencies +2. **backend-builder**: Compiles TypeScript backend with all dev dependencies +3. **production**: Combines compiled outputs with production dependencies only (`--omit=dev`) ### Security Features - Runs as non-root user (`node`) - Health check endpoint configured -- Minimal production dependencies (`--omit=dev`) +- Minimal production dependencies - Alpine Linux base (smaller attack surface) +- No unnecessary dev tools in production image + +### Multer v2.1.0 Upgrade + +Upgraded from v1.4.5 (no longer available) to v2.1.0: +- ✅ Security fixes (CVE-2026-2359 and others) +- ✅ Backward compatible API +- ✅ Better performance +- ✅ Active maintenance --- **Created**: 2026-03-08 **Branch**: `fix/add-package-lockfiles` **Status**: Ready to merge -**Issue**: Docker build failing with missing package-lock.json ✅ RESOLVED \ No newline at end of file +**Issue**: Docker build failing ✅ RESOLVED \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 7fc2f6a..ec965bd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,12 +6,9 @@ WORKDIR /app/frontend # Copy frontend package files COPY frontend/package*.json ./ -# Generate lockfile if it doesn't exist, then install dependencies -RUN if [ ! -f package-lock.json ]; then \ - echo "Generating package-lock.json..."; \ - npm install --package-lock-only; \ - fi && \ - npm ci +# Install all dependencies (including devDependencies for build) +# Use npm install which works without lockfile +RUN npm install # Copy frontend source and build COPY frontend/ ./ @@ -25,12 +22,8 @@ WORKDIR /app/backend # Copy backend package files COPY backend/package*.json ./ -# Generate lockfile if it doesn't exist, then install dependencies -RUN if [ ! -f package-lock.json ]; then \ - echo "Generating package-lock.json..."; \ - npm install --package-lock-only; \ - fi && \ - npm ci +# Install all dependencies (including TypeScript) +RUN npm install # Copy backend source and compile TypeScript COPY backend/ ./ @@ -44,12 +37,8 @@ WORKDIR /app # Copy backend package files COPY backend/package*.json ./ -# Generate lockfile if it doesn't exist, then install production dependencies only -RUN if [ ! -f package-lock.json ]; then \ - echo "Generating package-lock.json..."; \ - npm install --package-lock-only; \ - fi && \ - npm ci --omit=dev && \ +# Install production dependencies only +RUN npm install --omit=dev && \ npm cache clean --force # Copy compiled backend from builder