From 99ef592f612980dd18479305488f571c252bed54 Mon Sep 17 00:00:00 2001 From: Raymond <42894676+ramonechen@users.noreply.github.com> Date: Thu, 23 Apr 2026 23:31:33 -0400 Subject: [PATCH 01/12] Use inclusion pattern in .dockerignore Instead of blacklisting files, I adopted the popular whitelist approach that really minifies build size by only keeping the bare necessities needed to build the image. --- .dockerignore | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/.dockerignore b/.dockerignore index df2f64d..3aada18 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,7 +1,17 @@ -node_modules -dist -build -.git -.env -docker-compose.yml -Dockerfile \ No newline at end of file +# Ignore all files + +* + +# Whitelist necessary build and config files from project root +!eslint.config.js +!index.html +!package-lock.json +!package.json +!tsconfig.app.json +!tsconfig.json +!tsconfig.node.json +!vite.config.ts + +# Whitelist source and public directories +!public/ +!src/ \ No newline at end of file From 1d789af8e47d27c58dfbdd0aa17d2f7e7e42dfb3 Mon Sep 17 00:00:00 2001 From: Raymond <42894676+ramonechen@users.noreply.github.com> Date: Thu, 23 Apr 2026 23:32:00 -0400 Subject: [PATCH 02/12] Add clarifying comments to Dockerfile --- Dockerfile | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index c993a69..cd32f16 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,18 @@ FROM node:24.14.0-alpine -WORKDIR /app -COPY package*.json ./ +# Store all application files in /app +WORKDIR /site + +# Copy package.json and package-lock.json to the container +COPY package*.json . + +# Install dependencies using npm ci for a clean install RUN npm ci + +# Copy the rest of the application code to the container COPY . . + EXPOSE 5173 +# Start the development server CMD ["npm", "run", "dev"] From 87344f484d3e72467f2956ab2ab8c38926267b89 Mon Sep 17 00:00:00 2001 From: Raymond <42894676+ramonechen@users.noreply.github.com> Date: Thu, 23 Apr 2026 23:40:00 -0400 Subject: [PATCH 03/12] Ensure npm uses 11.12.x in Dockerfile By default, Node v24.14.0 is bundled with an older version of npm. This line simply updates npm to the version expected by the project's package-lock.json. --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index cd32f16..a9f0b72 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,8 @@ FROM node:24.14.0-alpine +# Update npm to 11.12.x to comply with project's package-lock.json +RUN npm install -g npm@11.12.0 + # Store all application files in /app WORKDIR /site From 662c0afe263584938e80305cd4d3aa701e8cf187 Mon Sep 17 00:00:00 2001 From: Raymond <42894676+ramonechen@users.noreply.github.com> Date: Thu, 23 Apr 2026 23:41:00 -0400 Subject: [PATCH 04/12] Clean Compose file Gave the Compose stack and container explicit names, and overall made the file format consistent with the API and SIS scraper configurations. --- docker-compose.yml | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 5838df7..68a9132 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,17 +1,20 @@ +name: carpi-site-dev-environment + services: - web: + site: build: . - image: carpi-dev:latest - - # expose port 5173 + image: carpi-site:latest + container_name: carpi-site + # Ensure image is built locally and is not pulled from a registry + pull_policy: build + restart: on-failure + # Enable interactive mode to allow scraper command input and output + stdin_open: true + tty: true ports: - "5173:5173" - - # mount the current directory to /app in the container + # Mount local code over the code built into the container for automatic + # reloading during development. volumes: - - .:/app - - /app/node_modules - - # interactive terminal - stdin_open: true - tty: true + - .:/site + - /site/node_modules From c5086bee837b932dfb66d4109261aed33d3839c1 Mon Sep 17 00:00:00 2001 From: Raymond <42894676+ramonechen@users.noreply.github.com> Date: Thu, 23 Apr 2026 23:42:54 -0400 Subject: [PATCH 05/12] Create image-publish.yml --- .github/workflows/image-publish.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/image-publish.yml diff --git a/.github/workflows/image-publish.yml b/.github/workflows/image-publish.yml new file mode 100644 index 0000000..821f84e --- /dev/null +++ b/.github/workflows/image-publish.yml @@ -0,0 +1,17 @@ +name: Build and Push Docker Image to GHCR + +# Run this workflow every time code is pushed or merged to the main branch +on: + # Temporary test trigger + ["push"] + # push: + # branches: + # - main + +jobs: + call-build-and-push: + uses: project-carpi/.github/.github/workflows/image-publish.yml@main + permissions: + contents: read + packages: write + secrets: inherit From 54a97ad4194dbe73d37f5fd6086e981771b72724 Mon Sep 17 00:00:00 2001 From: Raymond <42894676+ramonechen@users.noreply.github.com> Date: Thu, 23 Apr 2026 23:45:01 -0400 Subject: [PATCH 06/12] Restrict image-publish.yml to merges with main --- .github/workflows/image-publish.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/image-publish.yml b/.github/workflows/image-publish.yml index 821f84e..3136378 100644 --- a/.github/workflows/image-publish.yml +++ b/.github/workflows/image-publish.yml @@ -2,11 +2,9 @@ name: Build and Push Docker Image to GHCR # Run this workflow every time code is pushed or merged to the main branch on: - # Temporary test trigger - ["push"] - # push: - # branches: - # - main + push: + branches: + - main jobs: call-build-and-push: From 66508faa58debf126177285fa50a6c3ce1d36412 Mon Sep 17 00:00:00 2001 From: Raymond <42894676+ramonechen@users.noreply.github.com> Date: Fri, 24 Apr 2026 02:19:51 -0400 Subject: [PATCH 07/12] Add top-level comment to Compose file --- docker-compose.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 68a9132..99c7e53 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,21 @@ +# Docker Compose configuration for the CARPI website development environment. +# +# To start the development environment, run: +# docker compose run --rm --service-ports site +# +# This will build the Docker image for the site service if it doesn't already +# exist, and then start the container. An interactive terminal will be attached +# to the container so Vite commands can be entered. +# +# To stop the development environment, use Q+Enter in the terminal. The +# container will automatically be removed after it stops due to the --rm flag. +# +# Alternatively, you can run the container in detached (background) mode with: +# docker compose up -d +# +# To stop the detached container, run: +# docker compose down + name: carpi-site-dev-environment services: From c940650f765423e529dd7c1f0446e1a04eca63c8 Mon Sep 17 00:00:00 2001 From: Raymond <42894676+ramonechen@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:38:57 -0400 Subject: [PATCH 08/12] Fix incorrect comment in Compose file --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 99c7e53..f111943 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,7 +26,7 @@ services: # Ensure image is built locally and is not pulled from a registry pull_policy: build restart: on-failure - # Enable interactive mode to allow scraper command input and output + # Enable interactive mode to allow Vite command input and output stdin_open: true tty: true ports: From 3704b91dc92b0c2d688b50774ec7a9bdc4815aa1 Mon Sep 17 00:00:00 2001 From: Raymond <42894676+ramonechen@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:39:10 -0400 Subject: [PATCH 09/12] Repurpose Dockerfile for production --- Dockerfile | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index a9f0b72..4972bf5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,10 @@ FROM node:24.14.0-alpine # Update npm to 11.12.x to comply with project's package-lock.json RUN npm install -g npm@11.12.0 -# Store all application files in /app +# Install serve globally to serve the built application +RUN npm install -g serve + +# Store all application files in /site WORKDIR /site # Copy package.json and package-lock.json to the container @@ -15,7 +18,10 @@ RUN npm ci # Copy the rest of the application code to the container COPY . . +# Build the application for production +RUN npm run build + EXPOSE 5173 # Start the development server -CMD ["npm", "run", "dev"] +CMD ["npx", "serve", "-s", "dist", "-l", "5173"] From 694f60ed0a4b5e264642715525eb14136a59a9e7 Mon Sep 17 00:00:00 2001 From: Raymond <42894676+ramonechen@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:39:27 -0400 Subject: [PATCH 10/12] Override production command in Compose for development --- docker-compose.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index f111943..71f4731 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,6 +31,9 @@ services: tty: true ports: - "5173:5173" + # Override the Dockerfile production command with the development command + # for Vite. + command: npm run dev # Mount local code over the code built into the container for automatic # reloading during development. volumes: From cb3fceb7d6b4f49f43fe7fbec2fbb48528240daf Mon Sep 17 00:00:00 2001 From: Raymond <42894676+ramonechen@users.noreply.github.com> Date: Fri, 24 Apr 2026 13:52:15 -0400 Subject: [PATCH 11/12] Use two-stage builds in Dockerfile To separate dev and prod builds cleanly. --- Dockerfile | 21 ++++++++++++++++----- docker-compose.yml | 7 +++---- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4972bf5..46b4cb1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,8 @@ -FROM node:24.14.0-alpine +FROM node:24.14.0-alpine AS base # Update npm to 11.12.x to comply with project's package-lock.json RUN npm install -g npm@11.12.0 -# Install serve globally to serve the built application -RUN npm install -g serve - # Store all application files in /site WORKDIR /site @@ -18,10 +15,24 @@ RUN npm ci # Copy the rest of the application code to the container COPY . . +# --- DEVELOPMENT STAGE --- +FROM base AS dev + +EXPOSE 5173 + +# Start the development server +CMD ["npm", "run", "dev"] + +# --- PRODUCTION STAGE --- +FROM base AS prod + +# Install serve globally to serve the built application +RUN npm install -g serve + # Build the application for production RUN npm run build EXPOSE 5173 -# Start the development server +# Start the production server CMD ["npx", "serve", "-s", "dist", "-l", "5173"] diff --git a/docker-compose.yml b/docker-compose.yml index 71f4731..98d9b1b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,7 +20,9 @@ name: carpi-site-dev-environment services: site: - build: . + build: + context: . + target: dev image: carpi-site:latest container_name: carpi-site # Ensure image is built locally and is not pulled from a registry @@ -31,9 +33,6 @@ services: tty: true ports: - "5173:5173" - # Override the Dockerfile production command with the development command - # for Vite. - command: npm run dev # Mount local code over the code built into the container for automatic # reloading during development. volumes: From 75dd4ebee259214fdb9ecbde2c041b20bd60126e Mon Sep 17 00:00:00 2001 From: Raymond Chen <42894676+ramonechen@users.noreply.github.com> Date: Tue, 28 Apr 2026 17:48:39 -0400 Subject: [PATCH 12/12] Use NGINX instead of serve for production The serve application is very inefficient compared to using NGINX to serve the static files built by `npm build`. Therefore, this commit aims to eliminate the use of serve and the entire Node.js runtime in the production image produced by the Dockerfile. A new build stage has been added to the Dockerfile to build the static files that will be served in production. The production stage has been refactored to use a fresh image with only NGINX to serve those files from the build stage, eliminating the weight of the Node.js runtime. A custom nginx.conf has also been added to provide basic server configuration for the new NGINX server. It simply listens on port 80, and serves the static files within the container. --- .dockerignore | 1 + Dockerfile | 29 ++++++++++++----------------- nginx.conf | 11 +++++++++++ 3 files changed, 24 insertions(+), 17 deletions(-) create mode 100644 nginx.conf diff --git a/.dockerignore b/.dockerignore index 3aada18..e147338 100644 --- a/.dockerignore +++ b/.dockerignore @@ -11,6 +11,7 @@ !tsconfig.json !tsconfig.node.json !vite.config.ts +!nginx.conf # Whitelist source and public directories !public/ diff --git a/Dockerfile b/Dockerfile index 46b4cb1..a89bb0c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,38 +1,33 @@ +# --- BASE STAGE --- FROM node:24.14.0-alpine AS base - # Update npm to 11.12.x to comply with project's package-lock.json RUN npm install -g npm@11.12.0 - # Store all application files in /site WORKDIR /site - # Copy package.json and package-lock.json to the container COPY package*.json . - # Install dependencies using npm ci for a clean install RUN npm ci - # Copy the rest of the application code to the container COPY . . # --- DEVELOPMENT STAGE --- FROM base AS dev - EXPOSE 5173 - # Start the development server CMD ["npm", "run", "dev"] -# --- PRODUCTION STAGE --- -FROM base AS prod - -# Install serve globally to serve the built application -RUN npm install -g serve - +# --- BUILD STAGE --- +FROM base AS build # Build the application for production RUN npm run build -EXPOSE 5173 - -# Start the production server -CMD ["npx", "serve", "-s", "dist", "-l", "5173"] +# --- PRODUCTION STAGE --- +# Start from a clean NGINX image for production +FROM nginx:alpine AS prod +# Copy custom NGINX configuration to serve the built application +COPY nginx.conf /etc/nginx/conf.d/default.conf +# Copy the built application from the build stage +COPY --from=build /site/dist /usr/share/nginx/html +EXPOSE 80 +# NGINX starts automatically when the container runs diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..694aa12 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,11 @@ +server { + listen 80; + server_name localhost; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + # Fallback to index.html if the path isn't a real file + try_files $uri $uri/ /index.html; + } +} \ No newline at end of file