diff --git a/CHANGELOG.md b/CHANGELOG.md index 90f5d30..d133b67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) ### Changed - 2026-05-26 — `make update-mitre` upgraded from no-op placeholder to a real `curl` + optional container restart (Sprint 1 marker resolved). - 2026-05-26 — `EngagementDetailPage` no longer renders the "Simulations à venir au Sprint 2" placeholder; it embeds `` instead. +- 2026-05-26 — Makefile now auto-detects the container engine (`CONTAINER_CMD ?= docker || podman`) instead of hard-coding `docker`. Override with `make CONTAINER_CMD=podman` or `export CONTAINER_CMD=…`. The matching e2e tests (`us1`, `us6`) were updated to mirror the same detection so they pass on podman-only machines without an explicit `MIMIC_CONTAINER_CMD` export. --- diff --git a/Makefile b/Makefile index 10f4e8e..e701465 100644 --- a/Makefile +++ b/Makefile @@ -3,16 +3,20 @@ IMAGE ?= mimic:latest CONTAINER ?= mimic VOLUME ?= mimic-data +# Container engine: auto-detect docker first, fall back to podman. +# Override explicitly with `make CONTAINER_CMD=podman` or `export CONTAINER_CMD=podman`. +CONTAINER_CMD ?= $(shell if command -v docker >/dev/null 2>&1; then echo docker; else echo podman; fi) + .PHONY: build start stop restart update logs create-admin update-mitre test-backend test-frontend test-e2e clean build: - docker build -f docker/Dockerfile -t $(IMAGE) . + $(CONTAINER_CMD) build -f docker/Dockerfile -t $(IMAGE) . start: - docker run -d --name $(CONTAINER) -p $(PORT):5000 -v $(VOLUME):/data --env-file .env $(IMAGE) + $(CONTAINER_CMD) run -d --name $(CONTAINER) -p $(PORT):5000 -v $(VOLUME):/data --env-file .env $(IMAGE) stop: - docker stop $(CONTAINER) && docker rm $(CONTAINER) + $(CONTAINER_CMD) stop $(CONTAINER) && $(CONTAINER_CMD) rm $(CONTAINER) restart: $(MAKE) stop && $(MAKE) start @@ -21,7 +25,7 @@ update: git pull && $(MAKE) build && $(MAKE) restart logs: - docker logs -f $(CONTAINER) + $(CONTAINER_CMD) logs -f $(CONTAINER) create-admin: ifndef USER @@ -30,7 +34,7 @@ endif ifndef PASS $(error PASS is required: make create-admin USER=alice PASS=p4ssw0rd) endif - docker exec $(CONTAINER) flask create-admin $(USER) $(PASS) + $(CONTAINER_CMD) exec $(CONTAINER) flask create-admin $(USER) $(PASS) MITRE_URL ?= https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json @@ -38,13 +42,13 @@ update-mitre: @mkdir -p backend/data/mitre @curl -fsSL "$(MITRE_URL)" -o backend/data/mitre/enterprise-attack.json @echo "MITRE bundle updated" - @if docker ps --format '{{.Names}}' | grep -q "^$(CONTAINER)$$"; then \ + @if $(CONTAINER_CMD) ps --format '{{.Names}}' | grep -q "^$(CONTAINER)$$"; then \ echo "Restarting $(CONTAINER) to reload MITRE bundle..."; \ - docker restart $(CONTAINER); \ + $(CONTAINER_CMD) restart $(CONTAINER); \ fi test-backend: - docker exec $(CONTAINER) pytest -q backend/tests/ + $(CONTAINER_CMD) exec $(CONTAINER) pytest -q backend/tests/ test-frontend: cd frontend && npm run test -- --run @@ -53,6 +57,6 @@ test-e2e: cd e2e && npx playwright test clean: - -docker rm -f $(CONTAINER) 2>/dev/null - -docker volume rm $(VOLUME) 2>/dev/null + -$(CONTAINER_CMD) rm -f $(CONTAINER) 2>/dev/null + -$(CONTAINER_CMD) volume rm $(VOLUME) 2>/dev/null rm -rf backend/__pycache__ frontend/node_modules frontend/dist diff --git a/README.md b/README.md index 62ea2c7..e9dd2cc 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ mimic/ | Target | What it does | |---|---| -| `make build` | Build the `mimic:latest` Docker image (multistage: Node → Python) | +| `make build` | Build the `mimic:latest` container image (multistage: Node → Python). Uses `docker` if installed, otherwise `podman` — override with `make build CONTAINER_CMD=podman` | | `make start` | Start the container (port from `PORT`, default 5000; mounts `mimic-data` volume) | | `make stop` | Stop and remove the container | | `make restart` | `make stop && make start` — preserves the SQLite volume | diff --git a/e2e/tests/us1-bootstrap-admin.spec.ts b/e2e/tests/us1-bootstrap-admin.spec.ts index 785bfc1..9162336 100644 --- a/e2e/tests/us1-bootstrap-admin.spec.ts +++ b/e2e/tests/us1-bootstrap-admin.spec.ts @@ -19,7 +19,15 @@ import { adminToken, deleteUserByUsername, login, makeClient } from '../fixtures const __dirname = dirname(fileURLToPath(import.meta.url)); -const RUNTIME = process.env.MIMIC_CONTAINER_CMD ?? 'docker'; +function detectRuntime(): string { + try { + execSync('command -v docker', { stdio: 'ignore' }); + return 'docker'; + } catch { + return 'podman'; + } +} +const RUNTIME = process.env.MIMIC_CONTAINER_CMD ?? detectRuntime(); const CONTAINER = process.env.MIMIC_CONTAINER ?? 'mimic'; function runCreateAdmin(user: string, pass: string): { @@ -100,10 +108,11 @@ test.describe('US-1 — bootstrap first admin', () => { const token = await adminToken(); expect(token).toBeTruthy(); - // Defence-in-depth: assert the Makefile target literally invokes - // `docker exec … flask create-admin`. This is a contract check. + // Defence-in-depth: assert the Makefile target wraps an `exec … flask create-admin` + // through the container engine. Sprint 2 made the engine configurable via + // $(CONTAINER_CMD) (auto-detects docker or podman), so we assert the variable form. const makefilePath = resolve(__dirname, '../..', 'Makefile'); const content = readFileSync(makefilePath, 'utf8'); - expect(content).toMatch(/docker exec .+ flask create-admin/); + expect(content).toMatch(/\$\(CONTAINER_CMD\) exec .+ flask create-admin/); }); }); diff --git a/e2e/tests/us6-deployment.spec.ts b/e2e/tests/us6-deployment.spec.ts index eec1aa5..a068ff0 100644 --- a/e2e/tests/us6-deployment.spec.ts +++ b/e2e/tests/us6-deployment.spec.ts @@ -24,7 +24,15 @@ import { waitForHealth, } from '../fixtures/api'; -const RUNTIME = process.env.MIMIC_CONTAINER_CMD ?? 'docker'; +function detectRuntime(): string { + try { + execSync('command -v docker', { stdio: 'ignore' }); + return 'docker'; + } catch { + return 'podman'; + } +} +const RUNTIME = process.env.MIMIC_CONTAINER_CMD ?? detectRuntime(); const CONTAINER = process.env.MIMIC_CONTAINER ?? 'mimic'; const IMAGE = process.env.MIMIC_IMAGE ?? 'mimic:latest'; const __dirname = dirname(fileURLToPath(import.meta.url));