diff --git a/.env.example b/.env.example index 5a725fb..b04e5c4 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,5 @@ -# River Rooks Blog Environment Template +# Environment Template # Copy to .env and adjust variables as needed: SITE_DOMAIN=riverrooks.dev SITE_URL=https://riverrooks.dev +PORT=8080 diff --git a/Dockerfile b/Dockerfile index 124fb5d..18993f5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,9 +5,10 @@ WORKDIR /app # Dynamic build arguments & environment variables ARG SITE_DOMAIN ARG SITE_URL +ARG PORT=8080 ENV SITE_DOMAIN=$SITE_DOMAIN ENV SITE_URL=$SITE_URL -ENV PORT=8080 +ENV PORT=$PORT ENV NODE_ENV=production # Install dependencies @@ -20,8 +21,8 @@ COPY . . # Build static production assets RUN npm run build -# Expose web server port -EXPOSE 8080 +# Expose web server port dynamically +EXPOSE ${PORT} # Serve site with npm CMD ["npm", "start"] diff --git a/docker-compose.yml b/docker-compose.yml index 53378ab..43a1486 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,16 +8,18 @@ services: args: SITE_DOMAIN: ${SITE_DOMAIN:?Error: SITE_DOMAIN is not set. Please define SITE_DOMAIN in .env} SITE_URL: ${SITE_URL:-https://${SITE_DOMAIN}} + PORT: ${PORT:-8080} image: river-rooks-blog:latest container_name: river_rooks_web restart: unless-stopped environment: - SITE_DOMAIN=${SITE_DOMAIN:?Error: SITE_DOMAIN is not set. Please define SITE_DOMAIN in .env} - SITE_URL=${SITE_URL:-https://${SITE_DOMAIN}} + - PORT=${PORT:-8080} ports: - - "8080:8080" + - "${HOST_PORT:-${PORT:-8080}}:${PORT:-8080}" healthcheck: - test: ["CMD", "wget", "-qO-", "http://localhost:8080/healthz"] + test: ["CMD", "wget", "-qO-", "http://localhost:${PORT:-8080}/healthz"] interval: 30s timeout: 5s retries: 3 diff --git a/eleventy.config.js b/eleventy.config.js index 2afa0cd..b358e08 100644 --- a/eleventy.config.js +++ b/eleventy.config.js @@ -62,6 +62,11 @@ export default function (eleventyConfig) { return [...tagSet].sort(); }); + // 6. Dev Server Options (dynamic port binding) + eleventyConfig.setServerOptions({ + port: parseInt(process.env.PORT || "8080", 10) + }); + return { dir: { input: "src", diff --git a/test/docker-config.test.js b/test/docker-config.test.js index 6ae1ab7..991fd93 100644 --- a/test/docker-config.test.js +++ b/test/docker-config.test.js @@ -18,7 +18,9 @@ test("Docker Configuration: Dockerfile enforces Node runtime and excludes Nginx" // Must build static site and serve with npm assert.match(content, /RUN\s+npm\s+run\s+build/, "Dockerfile must run npm build"); - assert.match(content, /EXPOSE\s+8080/, "Dockerfile must expose port 8080"); + assert.match(content, /ARG\s+PORT=8080/, "Dockerfile must define ARG PORT with default fallback"); + assert.match(content, /ENV\s+PORT=\$\{?PORT\}?/, "Dockerfile must define ENV PORT using variable"); + assert.match(content, /EXPOSE\s+\$\{?PORT\}?/, "Dockerfile must expose dynamic PORT variable"); assert.match(content, /CMD\s*\[\s*["']npm["']\s*,\s*["']start["']\s*\]/, "Dockerfile must start via npm start"); }); @@ -28,11 +30,12 @@ test("Docker Configuration: docker-compose.yml configures Node service correctly const content = fs.readFileSync(composePath, "utf-8"); - // Verifies port mapping to 8080 - assert.match(content, /8080:8080/, "docker-compose.yml must map port 8080 to container 8080"); + // Verifies dynamic port mapping and environment variables + assert.match(content, /PORT:\s*\$\{PORT:-8080\}/, "docker-compose.yml must pass dynamic PORT build arg and env"); + assert.match(content, /:\$\{PORT:-8080\}/, "docker-compose.yml must map dynamic container PORT"); - // Verifies healthcheck targets 8080 - assert.match(content, /localhost:8080/, "Healthcheck must probe port 8080"); + // Verifies healthcheck targets dynamic port + assert.match(content, /localhost:\$\{PORT:-8080\}\/healthz/, "Healthcheck must probe dynamic PORT on /healthz"); // Verifies domain argument enforcement assert.match(content, /SITE_DOMAIN/, "docker-compose.yml must require SITE_DOMAIN");