added engine seperation from posts and entrypoint for compose building

This commit is contained in:
venus
2026-09-13 21:12:06 -05:00
parent 04a39ca852
commit 42e7e0f1ff
5 changed files with 84 additions and 23 deletions
+8 -3
View File
@@ -17,12 +17,17 @@ RUN npm ci
# Copy application source files
COPY . .
RUN chmod +x /app/entrypoint.sh
# Build static production assets
# Pre-compile baseline static assets
RUN npm run build
# Expose web server port dynamically
EXPOSE ${PORT}
# Serve site with npm
CMD ["npm", "start"]
# Native container healthcheck probing IPv4 loopback
HEALTHCHECK --interval=30s --timeout=5s --start-period=3s --retries=3 \
CMD node -e "fetch('http://127.0.0.1:' + (process.env.PORT || 8080) + '/healthz').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"
# Serve site using dynamic entrypoint
ENTRYPOINT ["/app/entrypoint.sh"]
Executable
+20
View File
@@ -0,0 +1,20 @@
#!/bin/sh
# Pass-through mode: if arguments were provided, execute them directly
if [ "$#" -gt 0 ]; then
exec "$@"
fi
# Build static site from mounted or packaged markdown posts
echo "[blog-engine] Compiling static site with Eleventy..."
npm run build || exit 1
# Optional watch mode for zero-restart live note updates
if [ "${WATCH:-false}" = "true" ]; then
echo "[blog-engine] Starting live watch daemon for markdown changes..."
npx @11ty/eleventy --watch &
fi
# Start production static web server
echo "[blog-engine] Starting static server on port ${PORT:-8080}..."
exec node server.js
+27 -14
View File
@@ -1,25 +1,38 @@
services:
web:
build:
context: .
dockerfile: Dockerfile
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
# Pull prebuilt engine image from your Gitea registry
image: git.riverrooks.dev/venus/river-rooks-blog:latest
# Local build fallback (optional when building image locally)
# build:
# context: .
# dockerfile: Dockerfile
# 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}"
container_name: river_rooks_web
restart: unless-stopped
healthcheck:
test: ["CMD", "node", "test/healthcheck.js"]
interval: 30s
timeout: 3s
start_period: 3s
retries: 3
# Healthcheck is natively baked into the Dockerfile image metadata
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}"
- "WATCH=${WATCH:-false}"
volumes:
# Mount external markdown posts directly into the container
- "./posts:/app/src/posts:ro"
ports:
- "${HOST_PORT:-${PORT:-8080}}:${PORT:-8080}"
networks:
- backend
labels:
- "traefik.enable=true"
- "traefik.http.routers.blog.rule=Host(`riverrooks.dev`) || Host(`www.riverrooks.dev`)"
- "traefik.http.routers.blog.entrypoints=websecure"
- "traefik.http.routers.blog.tls.certresolver=basic"
- "traefik.http.services.blog.loadbalancer.server.port=8080"
- "traefik.docker.network=backend"
networks:
backend:
external: true
@@ -1,6 +1,6 @@
---
layout: layouts/post.njk
title: "Infrastructure Notes & Systems Deployment Guide"
title: "a post titel Notes & Systems Deployment Guide"
date: 2026-07-15
tags:
- homelab
+28 -5
View File
@@ -3,7 +3,7 @@ import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
test("Docker Configuration: Dockerfile enforces Node runtime and excludes Nginx", () => {
test("Docker Configuration: Dockerfile enforces Node runtime, healthcheck, and entrypoint", () => {
const dockerfilePath = path.resolve(process.cwd(), "Dockerfile");
assert.ok(fs.existsSync(dockerfilePath), "Dockerfile must exist");
@@ -16,12 +16,32 @@ test("Docker Configuration: Dockerfile enforces Node runtime and excludes Nginx"
assert.doesNotMatch(content, /FROM\s+nginx/i, "Dockerfile must not use nginx base image");
assert.doesNotMatch(content, /nginx/i, "Dockerfile must not contain nginx directives");
// Must build static site and serve with npm
assert.match(content, /RUN\s+npm\s+run\s+build/, "Dockerfile must run npm build");
// Must build static site and configure dynamic port
assert.match(content, /RUN\s+npm\s+run\s+build/, "Dockerfile must compile baseline static assets");
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");
// Must define built-in HEALTHCHECK probing 127.0.0.1 loopback
assert.match(content, /HEALTHCHECK[\s\S]*?127\.0\.0\.1/, "Dockerfile must define baked-in HEALTHCHECK on 127.0.0.1");
// Must configure dynamic entrypoint script
assert.match(content, /ENTRYPOINT\s*\[\s*["']\/app\/entrypoint\.sh["']\s*\]/, "Dockerfile must use /app/entrypoint.sh entrypoint");
});
test("Docker Configuration: entrypoint.sh script exists and has valid structure", () => {
const entrypointPath = path.resolve(process.cwd(), "entrypoint.sh");
assert.ok(fs.existsSync(entrypointPath), "entrypoint.sh must exist");
const stats = fs.statSync(entrypointPath);
// Check executable permissions (at least owner executable: 0o100)
assert.ok(Boolean(stats.mode & 0o111), "entrypoint.sh must have executable permission bits");
const content = fs.readFileSync(entrypointPath, "utf-8");
assert.match(content, /^#!\/bin\/sh/, "entrypoint.sh must use POSIX /bin/sh shebang");
assert.match(content, /exec\s+"\$@"/, "entrypoint.sh must pass through custom container arguments");
assert.match(content, /npm\s+run\s+build/, "entrypoint.sh must build the static site");
assert.match(content, /exec\s+node\s+server\.js/, "entrypoint.sh must start static server with exec");
});
test("Docker Configuration: docker-compose configures Node service correctly", () => {
@@ -33,9 +53,12 @@ test("Docker Configuration: docker-compose configures Node service correctly", (
const content = fs.readFileSync(composePath, "utf-8");
// 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 configure PORT fallback");
assert.match(content, /:\$\{PORT:-8080\}/, "docker-compose.yml must map dynamic container PORT");
// Verifies domain argument enforcement
assert.match(content, /SITE_DOMAIN/, "docker-compose.yml must require SITE_DOMAIN");
// Verifies markdown volume mount
assert.match(content, /\/app\/src\/posts/, "docker-compose.yml must mount external markdown posts");
});