Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.git
node_modules
web-ui/node_modules
web-ui/dist
dist
coverage
docs
datacache
snapshots
.env
cache.sqlite
cache.sqlite-wal
cache.sqlite-shm
*.har
http-dump.jsonl
output.log
testout_*
21 changes: 21 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Dev image for ParksAPI — Node 24 / npm 11 (see package.json "engines")
FROM node:24-slim

ENV NPM_CONFIG_UPDATE_NOTIFIER=false \
NPM_CONFIG_FUND=false \
TZ=UTC

WORKDIR /app

# Install deps first so the layer is cached independently of the source.
# --ignore-scripts skips the root "prepare": "tsc" hook, which cannot run yet
# (no sources at this point) and is not needed: tsx executes TypeScript directly.
COPY package.json package-lock.json ./
RUN npm ci --ignore-scripts

# Baked-in sources, so the image is usable without a bind mount too.
COPY . .

COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
CMD ["npm", "run", "dev"]
103 changes: 103 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# ParksAPI — everything runs in a container (Node 24 / npm 11 inside).
# Nothing here needs Node installed on the host.
#
# make → this help
# make build → build the image once
# make park PARK=efteling
#
# Fedora ships Podman, not Docker. Override if you are on a Docker host:
# make COMPOSE="docker compose" dev

COMPOSE ?= podman-compose
SERVICE = parksapi
RUN = $(COMPOSE) run --rm $(SERVICE)

# Optional: extra flags forwarded to the test harness, e.g.
# make dev ARGS="--skip-schedules --verbose"
ARGS ?=
PARK ?=

.DEFAULT_GOAL := help

##@ Setup

.PHONY: build
build: ## Build the dev image (npm install happens here)
$(COMPOSE) build

.PHONY: rebuild
rebuild: ## Rebuild the image from scratch, ignoring layer cache
$(COMPOSE) build --no-cache

.PHONY: deps
deps: ## Reinstall node_modules inside the container volume
$(RUN) npm ci --ignore-scripts

##@ Running the harness

.PHONY: dev
dev: ## Test every park (long: 76 destinations, hits live APIs)
$(RUN) npm run dev -- $(ARGS)

.PHONY: park
park: ## Test one park — make park PARK=efteling
@test -n "$(PARK)" || { echo "usage: make park PARK=<parkId> (see: make list)"; exit 1; }
$(RUN) npm run dev -- $(PARK) $(ARGS)

.PHONY: category
category: ## Test a whole category — make category CATEGORY=Universal
@test -n "$(CATEGORY)" || { echo "usage: make category CATEGORY=<name>"; exit 1; }
$(RUN) npm run dev -- --category $(CATEGORY) $(ARGS)

.PHONY: list
list: ## List available park IDs
$(RUN) npm run dev -- --list

.PHONY: health
health: ## Health-check all endpoints
$(RUN) npm run health

##@ Build & tests

.PHONY: test
test: ## Run the Vitest suite
$(RUN) npm test

.PHONY: coverage
coverage: ## Run tests with coverage report
$(RUN) npm run test:coverage

.PHONY: compile
compile: ## Type-check / compile TypeScript to dist/
$(RUN) npm run build

.PHONY: web
web: ## Serve the web admin on http://localhost:8888
$(COMPOSE) run --rm --service-ports $(SERVICE) npm run web

.PHONY: shell
shell: ## Open a shell inside the container
$(RUN) bash

##@ Housekeeping

.PHONY: cache-clear
cache-clear: ## Drop the SQLite cache and retest everything fresh
$(RUN) npm run dev -- --clear-cache $(ARGS)

.PHONY: clean
clean: ## Remove containers and the node_modules volume
-$(COMPOSE) down -v

.PHONY: clean-all
clean-all: clean ## Also delete the built image
-podman rmi localhost/parksapi-dev

##@ Help

.PHONY: help
help: ## Show this help
@awk 'BEGIN {FS = ":.*##"; printf "\nParksAPI — containerised commands\n"} \
/^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5); next } \
/^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-14s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
@printf "\nFirst run: make build && make park PARK=efteling\n\n"
21 changes: 21 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
services:
parksapi:
build:
context: .
image: localhost/parksapi-dev
working_dir: /app
# :z = SELinux shared relabel (required on Fedora for bind mounts)
volumes:
- .:/app:z
# keep container-installed node_modules out of the host tree
- parksapi_node_modules:/app/node_modules
environment:
TZ: ${TZ:-UTC}
# only published by `make web` (podman-compose run --service-ports)
ports:
- "8888:8888"
tty: true
command: ["npm", "run", "dev"]

volumes:
parksapi_node_modules:
16 changes: 16 additions & 0 deletions docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/sh
set -e

# Every npm script runs `tsx --env-file=.env`, which aborts if the file is
# missing. An empty .env is a valid configuration (all @config props default
# to empty strings), so create it when absent — this is the `touch .env` step.
[ -f /app/.env ] || : > /app/.env

# When the source tree is bind-mounted, node_modules comes from a named volume
# that may still be empty on the very first run.
if [ ! -d /app/node_modules/tsx ]; then
echo "→ node_modules empty, running npm ci…"
npm ci --ignore-scripts
fi

exec "$@"