diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..8e627f5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,14 @@ +.git +.github +node_modules +dist +coverage +*.db +*.db-shm +*.db-wal +.env +.DS_Store +npm-debug.log +pnpm-debug.log +Dockerfile +docker-compose.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec1f63c..e5e88b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,3 +36,12 @@ jobs: - name: Replay smoke test run: pnpm run replay + + - name: Build Docker test stage + run: docker build --target test -t narrative-alpha-agent:test . + + - name: Build Docker runtime image + run: docker build -t narrative-alpha-agent:ci . + + - name: Docker replay smoke test + run: docker run --rm narrative-alpha-agent:ci diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d29cd3..1025e0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,3 +22,4 @@ The format is based on Keep a Changelog, and this project uses Conventional Comm - Synthetic demo dataset and CLI commands - Vitest coverage for clustering, scoring, replay, and lifecycle transitions - Open-source documentation, CI, and project governance files +- Dockerfile, Docker Compose, and containerized replay smoke test diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..61b5a69 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +# syntax=docker/dockerfile:1.7 + +FROM node:20-bookworm-slim AS base +WORKDIR /app +ENV PNPM_HOME=/pnpm +ENV PATH=$PNPM_HOME:$PATH +RUN corepack enable + +FROM base AS deps +RUN apt-get update \ + && apt-get install -y --no-install-recommends python3 make g++ \ + && rm -rf /var/lib/apt/lists/* +COPY package.json pnpm-lock.yaml ./ +RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store pnpm install --frozen-lockfile + +FROM deps AS test +COPY . . +RUN pnpm run check +RUN pnpm run replay + +FROM base AS runner +ENV NODE_ENV=production +COPY --from=deps /app/node_modules ./node_modules +COPY package.json pnpm-lock.yaml ./ +COPY src ./src +COPY README.md LICENSE ./ +RUN groupadd --system naa \ + && useradd --system --gid naa --home-dir /app naa \ + && mkdir -p /data \ + && chown -R naa:naa /app /data +USER naa +VOLUME ["/data"] +ENV NAA_DATABASE_PATH=/data/naa.sqlite +CMD ["pnpm", "run", "replay"] diff --git a/README.md b/README.md index 2f0f460..3c26a6b 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,13 @@ pnpm run ingest pnpm run dev ``` +Docker: + +```bash +docker build -t narrative-alpha-agent:local . +docker run --rm narrative-alpha-agent:local +``` + Quality gates: ```bash @@ -146,6 +153,7 @@ pnpm run test - [Providers and Secrets](docs/PROVIDERS.md) - [Observability](docs/OBSERVABILITY.md) - [Notifications](docs/NOTIFICATIONS.md) +- [Docker](docs/DOCKER.md) - [Contributing](CONTRIBUTING.md) - [Security](SECURITY.md) - [Changelog](CHANGELOG.md) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..fccdd9e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,21 @@ +services: + narrative-alpha-agent: + build: + context: . + target: runner + image: narrative-alpha-agent:local + environment: + NAA_ENV: development + NAA_DATABASE_PATH: /data/naa.sqlite + NAA_LLM_PROVIDER: local + LANGSMITH_TRACING: ${LANGSMITH_TRACING:-false} + LANGSMITH_API_KEY: ${LANGSMITH_API_KEY:-} + LANGSMITH_PROJECT: ${LANGSMITH_PROJECT:-narrative-alpha-agent} + DISCORD_WEBHOOK_URL: ${DISCORD_WEBHOOK_URL:-} + DISCORD_USERNAME: ${DISCORD_USERNAME:-Narrative Alpha Agent} + volumes: + - naa-data:/data + command: ["pnpm", "run", "replay"] + +volumes: + naa-data: diff --git a/docs/DOCKER.md b/docs/DOCKER.md new file mode 100644 index 0000000..c135791 --- /dev/null +++ b/docs/DOCKER.md @@ -0,0 +1,66 @@ +# Docker + +Narrative Alpha Agent includes Docker support for reproducible local runs, CI smoke tests, and deployment experiments. + +Docker is useful for this project because `better-sqlite3` uses native bindings. The image builds those bindings in a controlled Linux environment instead of relying on each contributor's local machine. + +## Build + +```bash +docker build -t narrative-alpha-agent:local . +``` + +The Dockerfile is multi-stage: + +- `deps`: installs dependencies and builds native packages +- `test`: runs `pnpm run check` and `pnpm run replay` +- `runner`: non-root runtime image with source, dependencies, and `/data` volume + +## Run Replay + +```bash +docker run --rm narrative-alpha-agent:local +``` + +By default the container runs: + +```bash +pnpm run replay +``` + +## Run Ingest With Persistent SQLite + +```bash +docker run --rm \ + -v naa-data:/data \ + -e NAA_DATABASE_PATH=/data/naa.sqlite \ + narrative-alpha-agent:local \ + pnpm run ingest +``` + +## Docker Compose + +```bash +docker compose up --build +``` + +Compose mounts a named volume at `/data` and runs replay with local deterministic providers by default. + +## Secrets + +Do not bake secrets into the image. Pass runtime secrets through environment variables: + +```bash +docker run --rm \ + -e LANGSMITH_TRACING=true \ + -e LANGSMITH_API_KEY=lsv2_... \ + -e LANGSMITH_PROJECT=narrative-alpha-agent \ + -e DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/... \ + narrative-alpha-agent:local +``` + +For provider credentials, use the same variables documented in `.env.example` and `docs/PROVIDERS.md`. + +## CI + +GitHub Actions builds the Docker image and runs a replay smoke test inside the container. This keeps the container path from drifting away from the normal local development path. diff --git a/docs/OPERATIONS.md b/docs/OPERATIONS.md index 2e14ff8..33aa494 100644 --- a/docs/OPERATIONS.md +++ b/docs/OPERATIONS.md @@ -8,6 +8,8 @@ pnpm run replay pnpm run ingest pnpm run dev pnpm run check +docker build -t narrative-alpha-agent:local . +docker run --rm narrative-alpha-agent:local ``` ## Data and Storage @@ -16,6 +18,8 @@ pnpm run check `pnpm run replay` uses an in-memory database by default and does not write persistent state. +Docker runtime uses `/data/naa.sqlite` by default for persistent SQLite state when running ingest. See [Docker](DOCKER.md). + ## Configuration Defaults live in `src/config/defaults.ts`.