refactor(compose): unify into single file with local/dev/demo profiles
Some checks failed
Holocron Frontend CI / lint-and-test (push) Has been cancelled
Some checks failed
Holocron Frontend CI / lint-and-test (push) Has been cancelled
K8s is the production target — docker compose is local-only. Collapse the three compose files (docker-compose.yml, .local.yml, .local-dev.yml) into a single docker-compose.yml driven by profiles: local → full stack in containers dev → backing services only; frontend on host (npm run start:dev) demo → synthetic MQTT publisher (composable with local or dev) Make targets: make up → --profile local --profile demo make up-dev → --profile dev --profile demo make up-bare → --profile local (no synthetic data) All values (MQTT_TOPIC_PREFIX, MAPTILER_KEY, KEYCLOAK_*, host port mappings) are env-driven with sensible local defaults. The same env contract is what the K8s ConfigMap will inject in production. Also: - Bump frontend Dockerfile (local/dev) from node:18 → node:20-alpine. Vite 7 requires crypto.hash (Node 20.12+). Also bumps the test-publisher base image to node:20-alpine. - Default host MQTT port moved to 1885 and frontend to 3002 (compose env defaults) to avoid common conflicts; both overridable via .env. Verified: - docker compose --profile local --profile demo config parses OK - docker compose --profile dev --profile demo config parses OK - `make up` brings up 6 healthy containers - curl localhost:7070/health → 200 - curl localhost:3002 → 200 - Test publisher streams UAV/SHIP/HELO/TANK positions on r2d2/holocron/... - Browser screenshot confirms Cesium map rendering with live MQTT assets Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
70a07a93c2
commit
88ffa3cad5
7 changed files with 134 additions and 268 deletions
35
.env.example
35
.env.example
|
|
@ -1,8 +1,35 @@
|
|||
# Copy to .env and set your keys. Do not commit .env.
|
||||
# MAPTILER_KEY=your_maptiler_api_key_here
|
||||
# MAPTILER_KEY=
|
||||
# Copy to .env and set your values. Do not commit .env.
|
||||
#
|
||||
# This same env contract is used by the K8s production deployment.
|
||||
# Production values come from a ConfigMap/Secret in the cluster; local values
|
||||
# come from this file.
|
||||
|
||||
# Optional: production / non-local
|
||||
# ─── MQTT topic prefix ──────────────────────────────────────────────
|
||||
# Local default: r2d2/holocron
|
||||
# Production: set per environment (e.g. r2d2-prod/holocron, nato/holocron/...)
|
||||
MQTT_TOPIC_PREFIX=r2d2/holocron
|
||||
|
||||
# ─── Map tiles ──────────────────────────────────────────────────────
|
||||
# Get a free key at https://maptiler.com
|
||||
MAPTILER_KEY=
|
||||
|
||||
# ─── Host port mappings (local docker compose only) ─────────────────
|
||||
# Override if these conflict with other services on your machine.
|
||||
PROXY_PORT=7070
|
||||
FRONTEND_PORT=3002
|
||||
WS_PORT=9002
|
||||
MQTT_PORT=1885
|
||||
MQTT_WS_PORT=9001
|
||||
|
||||
# ─── Keycloak ───────────────────────────────────────────────────────
|
||||
# Local defaults are fine; production overrides via Secret.
|
||||
KEYCLOAK_DB_NAME=keycloak_db
|
||||
KEYCLOAK_DB_USER=keycloakuser
|
||||
KEYCLOAK_DB_PASSWORD=keycloakuser
|
||||
KEYCLOAK_ADMIN=adminkeycloak
|
||||
KEYCLOAK_ADMIN_PASSWORD=adminkeycloak
|
||||
|
||||
# ─── Production-only (set in K8s ConfigMap, not here) ───────────────
|
||||
# CORS_ORIGIN=https://app.example.com,https://other.example.com
|
||||
# WS_PUBLIC_URL=wss://app.example.com/ws
|
||||
# MAX_MESSAGE_BYTES=524288
|
||||
|
|
|
|||
49
Makefile
49
Makefile
|
|
@ -1,23 +1,44 @@
|
|||
# HOLOCRON - Local development targets
|
||||
# R2D2-Holocron — local development targets
|
||||
#
|
||||
# Production deploys to Kubernetes, not docker compose. See docs/plans/ for K8s.
|
||||
#
|
||||
# Profiles defined in docker-compose.yml:
|
||||
# local → full stack in containers
|
||||
# dev → backing services only; frontend on host (npm run start:dev)
|
||||
# demo → synthetic MQTT publisher
|
||||
|
||||
.PHONY: up down build logs clean
|
||||
.PHONY: up up-dev up-bare down logs rebuild clean ps config
|
||||
|
||||
# Start the local Docker stack (builds images on first run)
|
||||
COMPOSE := docker compose
|
||||
|
||||
# Default: full local stack with demo data
|
||||
up:
|
||||
docker compose -f docker-compose.local.yml up --build -d
|
||||
$(COMPOSE) --profile local --profile demo up --build -d
|
||||
|
||||
# Dev mode: backing services in docker, frontend on host
|
||||
up-dev:
|
||||
$(COMPOSE) --profile dev --profile demo up --build -d
|
||||
@echo ""
|
||||
@echo "Now start the frontend on the host:"
|
||||
@echo " cd holocron-frontend && npm run start:dev"
|
||||
|
||||
# Local stack without synthetic data (for real MQTT feeds)
|
||||
up-bare:
|
||||
$(COMPOSE) --profile local up --build -d
|
||||
|
||||
# Stop the stack
|
||||
down:
|
||||
docker compose -f docker-compose.local.yml down
|
||||
$(COMPOSE) --profile local --profile dev --profile demo down
|
||||
|
||||
# View logs (follow)
|
||||
logs:
|
||||
docker compose -f docker-compose.local.yml logs -f
|
||||
$(COMPOSE) logs -f
|
||||
|
||||
# Rebuild and restart
|
||||
rebuild: down
|
||||
docker compose -f docker-compose.local.yml up --build -d
|
||||
rebuild: down up
|
||||
|
||||
# Remove containers and volumes
|
||||
clean: down
|
||||
docker compose -f docker-compose.local.yml down -v
|
||||
clean:
|
||||
$(COMPOSE) --profile local --profile dev --profile demo down -v
|
||||
|
||||
ps:
|
||||
$(COMPOSE) ps
|
||||
|
||||
config:
|
||||
$(COMPOSE) --profile local --profile demo config
|
||||
|
|
|
|||
|
|
@ -1,104 +0,0 @@
|
|||
---
|
||||
# Local development: all services in Docker EXCEPT the frontend.
|
||||
# Frontend runs on the host with hot-reload (Vite).
|
||||
#
|
||||
# 1. Start stack: docker compose -f docker-compose.local-dev.yml up --build
|
||||
# 2. Start frontend: cd holocron-frontend && npm run start:dev
|
||||
# 3. Open: http://localhost:7070 (proxy serves app from host:3333)
|
||||
#
|
||||
# Endpoints:
|
||||
# - App (via proxy from host): http://localhost:7070
|
||||
# - Keycloak: http://localhost:7070/auth
|
||||
# - Health: http://localhost:7070/health
|
||||
# - Mosquitto: 1883, 9001
|
||||
# - Frontend dev server: http://localhost:3333 (direct, optional)
|
||||
# - Frontend API/WS (on host): http://localhost:3001, ws://localhost:9002
|
||||
|
||||
services:
|
||||
holocron-keycloak-postgresql:
|
||||
image: postgres:15-alpine
|
||||
container_name: holocron-keycloak-postgresql-local
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- keycloak_postgres_data:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_DB: keycloak_db
|
||||
POSTGRES_USER: keycloakuser
|
||||
POSTGRES_PASSWORD: keycloakuser
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U keycloakuser -d keycloak_db"]
|
||||
interval: 15s
|
||||
timeout: 15s
|
||||
retries: 5
|
||||
|
||||
holocron-keycloak:
|
||||
build:
|
||||
context: ./holocron-keycloak
|
||||
dockerfile: Dockerfile.local.public
|
||||
image: holocron-keycloak:local
|
||||
container_name: holocron-keycloak-local
|
||||
restart: unless-stopped
|
||||
command:
|
||||
[
|
||||
"-Dkeycloak.migration.action=import",
|
||||
"-Dkeycloak.migration.provider=dir",
|
||||
"-Dkeycloak.migration.dir=/opt/keycloak/data/import",
|
||||
"-Dkeycloak.migration.strategy=IGNORE_EXISTING"
|
||||
]
|
||||
environment:
|
||||
KC_DB_URL: jdbc:postgresql://holocron-keycloak-postgresql-local/keycloak_db
|
||||
KC_DB_USERNAME: keycloakuser
|
||||
KC_DB_PASSWORD: keycloakuser
|
||||
KEYCLOAK_ADMIN: adminkeycloak
|
||||
KEYCLOAK_ADMIN_PASSWORD: adminkeycloak
|
||||
KC_HTTP_PORT: 8090
|
||||
depends_on:
|
||||
holocron-keycloak-postgresql:
|
||||
condition: service_healthy
|
||||
|
||||
holocron-proxy:
|
||||
build:
|
||||
context: ./holocron-proxy
|
||||
dockerfile: Dockerfile.local.public
|
||||
image: holocron-proxy:local
|
||||
container_name: holocron-proxy-local
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "7070:7070"
|
||||
volumes:
|
||||
- ./holocron-proxy/nginx/local-dev.conf:/etc/nginx/conf.d/default.conf
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
depends_on:
|
||||
- holocron-keycloak
|
||||
|
||||
mosquitto:
|
||||
image: eclipse-mosquitto:2
|
||||
container_name: holocron-mosquitto-local
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "1883:1883"
|
||||
- "9001:9001"
|
||||
volumes:
|
||||
- ./mosquitto/config:/mosquitto/config
|
||||
- mosquitto_data:/mosquitto/data
|
||||
- mosquitto_log:/mosquitto/log
|
||||
|
||||
test-publisher:
|
||||
image: node:18-alpine
|
||||
container_name: holocron-test-publisher-local
|
||||
working_dir: /app
|
||||
volumes:
|
||||
- ./holocron-cesium/test-publisher:/app
|
||||
command: sh -c "npm install && node publisher.js"
|
||||
environment:
|
||||
MQTT_BROKER: mqtt://mosquitto:1883
|
||||
MQTT_TOPIC_PREFIX: ${MQTT_TOPIC_PREFIX:-r2d2/holocron}
|
||||
depends_on:
|
||||
- mosquitto
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
keycloak_postgres_data:
|
||||
mosquitto_data:
|
||||
mosquitto_log:
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
---
|
||||
# Local development stack using PUBLIC Docker Hub images.
|
||||
# Run from project root: docker compose -f docker-compose.local.yml up --build
|
||||
#
|
||||
# Endpoints:
|
||||
# - Proxy: http://localhost:7070
|
||||
# - Health: http://localhost:7070/health
|
||||
# - Keycloak: http://localhost:7070/auth
|
||||
# - Frontend: http://localhost:3001
|
||||
# - WebSocket: ws://localhost:9002
|
||||
|
||||
services:
|
||||
holocron-keycloak-postgresql:
|
||||
image: postgres:15-alpine
|
||||
container_name: holocron-keycloak-postgresql-local
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- keycloak_postgres_data:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_DB: keycloak_db
|
||||
POSTGRES_USER: keycloakuser
|
||||
POSTGRES_PASSWORD: keycloakuser
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U keycloakuser -d keycloak_db"]
|
||||
interval: 15s
|
||||
timeout: 15s
|
||||
retries: 5
|
||||
|
||||
holocron-keycloak:
|
||||
build:
|
||||
context: ./holocron-keycloak
|
||||
dockerfile: Dockerfile.local.public
|
||||
image: holocron-keycloak:local
|
||||
container_name: holocron-keycloak-local
|
||||
restart: unless-stopped
|
||||
command:
|
||||
[
|
||||
"-Dkeycloak.migration.action=import",
|
||||
"-Dkeycloak.migration.provider=dir",
|
||||
"-Dkeycloak.migration.dir=/opt/keycloak/data/import",
|
||||
"-Dkeycloak.migration.strategy=IGNORE_EXISTING"
|
||||
]
|
||||
environment:
|
||||
KC_DB_URL: jdbc:postgresql://holocron-keycloak-postgresql-local/keycloak_db
|
||||
KC_DB_USERNAME: keycloakuser
|
||||
KC_DB_PASSWORD: keycloakuser
|
||||
KEYCLOAK_ADMIN: adminkeycloak
|
||||
KEYCLOAK_ADMIN_PASSWORD: adminkeycloak
|
||||
KC_HTTP_PORT: 8090
|
||||
depends_on:
|
||||
holocron-keycloak-postgresql:
|
||||
condition: service_healthy
|
||||
|
||||
holocron-proxy:
|
||||
build:
|
||||
context: ./holocron-proxy
|
||||
dockerfile: Dockerfile.local.public
|
||||
image: holocron-proxy:local
|
||||
container_name: holocron-proxy-local
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "7070:7070"
|
||||
depends_on:
|
||||
- holocron-keycloak
|
||||
|
||||
mosquitto:
|
||||
image: eclipse-mosquitto:2
|
||||
container_name: holocron-mosquitto-local
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "1883:1883"
|
||||
- "9001:9001"
|
||||
volumes:
|
||||
- ./mosquitto/config:/mosquitto/config
|
||||
- mosquitto_data:/mosquitto/data
|
||||
- mosquitto_log:/mosquitto/log
|
||||
|
||||
holocron-frontend:
|
||||
build:
|
||||
context: ./holocron-frontend
|
||||
dockerfile: Dockerfile.local
|
||||
image: holocron-frontend:local
|
||||
container_name: holocron-frontend-local
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- "3001:3001"
|
||||
- "9002:9002"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- PORT=3001
|
||||
- WS_PORT=9002
|
||||
- MQTT_BROKER=mqtt://mosquitto:1883
|
||||
- MQTT_TOPIC_PREFIX=${MQTT_TOPIC_PREFIX:-r2d2/holocron}
|
||||
- MAPTILER_KEY=${MAPTILER_KEY:-}
|
||||
depends_on:
|
||||
- mosquitto
|
||||
|
||||
# Sample data publisher: sends test assets to MQTT so the map shows UAVs, ships, etc.
|
||||
test-publisher:
|
||||
image: node:18-alpine
|
||||
container_name: holocron-test-publisher-local
|
||||
working_dir: /app
|
||||
volumes:
|
||||
- ./holocron-cesium/test-publisher:/app
|
||||
command: sh -c "npm install && node publisher.js"
|
||||
environment:
|
||||
MQTT_BROKER: mqtt://mosquitto:1883
|
||||
MQTT_TOPIC_PREFIX: ${MQTT_TOPIC_PREFIX:-r2d2/holocron}
|
||||
depends_on:
|
||||
- mosquitto
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
keycloak_postgres_data:
|
||||
mosquitto_data:
|
||||
mosquitto_log:
|
||||
|
|
@ -1,32 +1,50 @@
|
|||
---
|
||||
# Local development stack using PUBLIC Docker Hub images.
|
||||
# Run from project root: docker compose -f docker-compose.local.yml up --build
|
||||
# Unified docker-compose for R2D2-Holocron.
|
||||
#
|
||||
# Endpoints:
|
||||
# - Proxy: http://localhost:7070
|
||||
# - Health: http://localhost:7070/health
|
||||
# - Keycloak: http://localhost:7070/auth
|
||||
# - Frontend: http://localhost:3001
|
||||
# - WebSocket: ws://localhost:9002
|
||||
# Profiles:
|
||||
# local → full stack in containers (frontend served by Node from image)
|
||||
# dev → backing services only; run frontend on host with `npm run start:dev`
|
||||
# (proxy is configured to forward to host.docker.internal)
|
||||
# demo → adds test-publisher streaming synthetic MQTT assets
|
||||
#
|
||||
# Production deploys to Kubernetes (not this file). The env contract below
|
||||
# (MQTT_TOPIC_PREFIX, MAPTILER_KEY, KEYCLOAK_*, etc.) is the same contract the
|
||||
# K8s manifests will inject via ConfigMap/Secret. Keep it stable.
|
||||
#
|
||||
# Usage:
|
||||
# make up # local + demo (default)
|
||||
# make up-dev # dev + demo
|
||||
# docker compose --profile local up -d # local only (no test publisher)
|
||||
# docker compose --profile dev up -d # dev only
|
||||
#
|
||||
# Endpoints (local profile):
|
||||
# - Proxy: http://localhost:${PROXY_PORT:-7070}
|
||||
# - Health: http://localhost:${PROXY_PORT:-7070}/health
|
||||
# - Keycloak: http://localhost:${PROXY_PORT:-7070}/auth
|
||||
# - Frontend: http://localhost:${FRONTEND_PORT:-3002}
|
||||
# - WebSocket: ws://localhost:${WS_PORT:-9002}
|
||||
# - MQTT: tcp://localhost:${MQTT_PORT:-1885}
|
||||
|
||||
services:
|
||||
holocron-keycloak-postgresql:
|
||||
profiles: [local, dev]
|
||||
image: postgres:15-alpine
|
||||
container_name: holocron-keycloak-postgresql-local
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- keycloak_postgres_data:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_DB: keycloak_db
|
||||
POSTGRES_USER: keycloakuser
|
||||
POSTGRES_PASSWORD: keycloakuser
|
||||
POSTGRES_DB: ${KEYCLOAK_DB_NAME:-keycloak_db}
|
||||
POSTGRES_USER: ${KEYCLOAK_DB_USER:-keycloakuser}
|
||||
POSTGRES_PASSWORD: ${KEYCLOAK_DB_PASSWORD:-keycloakuser}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U keycloakuser -d keycloak_db"]
|
||||
test: ["CMD-SHELL", "pg_isready -U ${KEYCLOAK_DB_USER:-keycloakuser} -d ${KEYCLOAK_DB_NAME:-keycloak_db}"]
|
||||
interval: 15s
|
||||
timeout: 15s
|
||||
retries: 5
|
||||
|
||||
holocron-keycloak:
|
||||
profiles: [local, dev]
|
||||
build:
|
||||
context: ./holocron-keycloak
|
||||
dockerfile: Dockerfile.local.public
|
||||
|
|
@ -41,17 +59,18 @@ services:
|
|||
"-Dkeycloak.migration.strategy=IGNORE_EXISTING"
|
||||
]
|
||||
environment:
|
||||
KC_DB_URL: jdbc:postgresql://holocron-keycloak-postgresql-local/keycloak_db
|
||||
KC_DB_USERNAME: keycloakuser
|
||||
KC_DB_PASSWORD: keycloakuser
|
||||
KEYCLOAK_ADMIN: adminkeycloak
|
||||
KEYCLOAK_ADMIN_PASSWORD: adminkeycloak
|
||||
KC_DB_URL: jdbc:postgresql://holocron-keycloak-postgresql-local/${KEYCLOAK_DB_NAME:-keycloak_db}
|
||||
KC_DB_USERNAME: ${KEYCLOAK_DB_USER:-keycloakuser}
|
||||
KC_DB_PASSWORD: ${KEYCLOAK_DB_PASSWORD:-keycloakuser}
|
||||
KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN:-adminkeycloak}
|
||||
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD:-adminkeycloak}
|
||||
KC_HTTP_PORT: 8090
|
||||
depends_on:
|
||||
holocron-keycloak-postgresql:
|
||||
condition: service_healthy
|
||||
|
||||
holocron-proxy:
|
||||
profiles: [local]
|
||||
build:
|
||||
context: ./holocron-proxy
|
||||
dockerfile: Dockerfile.local.public
|
||||
|
|
@ -59,23 +78,42 @@ services:
|
|||
container_name: holocron-proxy-local
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "7070:7070"
|
||||
- "${PROXY_PORT:-7070}:7070"
|
||||
depends_on:
|
||||
- holocron-keycloak
|
||||
|
||||
holocron-proxy-dev:
|
||||
profiles: [dev]
|
||||
build:
|
||||
context: ./holocron-proxy
|
||||
dockerfile: Dockerfile.local.public
|
||||
image: holocron-proxy:local
|
||||
container_name: holocron-proxy-local
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${PROXY_PORT:-7070}:7070"
|
||||
volumes:
|
||||
- ./holocron-proxy/nginx/local-dev.conf:/etc/nginx/conf.d/default.conf
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
depends_on:
|
||||
- holocron-keycloak
|
||||
|
||||
mosquitto:
|
||||
profiles: [local, dev]
|
||||
image: eclipse-mosquitto:2
|
||||
container_name: holocron-mosquitto-local
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "1883:1883"
|
||||
- "9001:9001"
|
||||
- "${MQTT_PORT:-1885}:1883"
|
||||
- "${MQTT_WS_PORT:-9001}:9001"
|
||||
volumes:
|
||||
- ./mosquitto/config:/mosquitto/config
|
||||
- mosquitto_data:/mosquitto/data
|
||||
- mosquitto_log:/mosquitto/log
|
||||
|
||||
holocron-frontend:
|
||||
profiles: [local]
|
||||
build:
|
||||
context: ./holocron-frontend
|
||||
dockerfile: Dockerfile.local
|
||||
|
|
@ -85,8 +123,8 @@ services:
|
|||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- "3001:3001"
|
||||
- "9002:9002"
|
||||
- "${FRONTEND_PORT:-3002}:3001"
|
||||
- "${WS_PORT:-9002}:9002"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- PORT=3001
|
||||
|
|
@ -97,9 +135,11 @@ services:
|
|||
depends_on:
|
||||
- mosquitto
|
||||
|
||||
# Sample data publisher: sends test assets to MQTT so the map shows UAVs, ships, etc.
|
||||
# Synthetic asset publisher (UAVs, ships, helos, tanks moving across Europe).
|
||||
# Useful for both `local` and `dev` profiles when no real MQTT feed is wired up.
|
||||
test-publisher:
|
||||
image: node:18-alpine
|
||||
profiles: [demo]
|
||||
image: node:20-alpine
|
||||
container_name: holocron-test-publisher-local
|
||||
working_dir: /app
|
||||
volumes:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Stage 1: Build React application
|
||||
FROM node:18-alpine AS builder
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ COPY . .
|
|||
RUN npm run build
|
||||
|
||||
# Stage 2: Production image
|
||||
FROM node:18-alpine
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Stage 1: Build React application
|
||||
FROM node:18-alpine AS builder
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ COPY . .
|
|||
RUN npm run build
|
||||
|
||||
# Stage 2: Production image
|
||||
FROM node:18-alpine
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue