Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@ cypress/
.cursor/
.husky/
prisma/*.pem
# Untracked local tooling, absent from any CI build: keeps a locally built
# image identical to the published one.
.claude/
.plan/
.venv/
20 changes: 20 additions & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches: [main]
paths:
- "Dockerfile"
- ".dockerignore"
- ".nvmrc"
- "docker-compose.yml"
- "docker-entrypoint.sh"
Expand Down Expand Up @@ -33,3 +34,22 @@ jobs:

- name: Build Docker image
run: docker build -t oss-oopssec-store:test .

# Offline, so a package missing from the image fails here instead of
# being fetched from the registry at startup.
- name: Smoke test the image offline
timeout-minutes: 5
run: |
docker run -d --name smoke --network none oss-oopssec-store:test
until docker logs smoke 2>&1 | grep -F "Database initialized successfully." >/dev/null; do
[ "$(docker inspect -f '{{.State.Running}}' smoke)" = true ] || exit 1
sleep 2
done
until docker exec smoke wget -qO- http://127.0.0.1:3000/api/flags/count; do
[ "$(docker inspect -f '{{.State.Running}}' smoke)" = true ] || exit 1
sleep 2
done

- name: Show container logs
if: failure()
run: docker logs smoke
23 changes: 17 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
# Keep this default in sync with .nvmrc; tests/unit/node-version-parity.test.ts
# fails the build if they drift.
ARG NODE_VERSION=22
FROM node:${NODE_VERSION}-alpine

RUN apk add --no-cache python3 make g++ gcc libxml2-dev libxslt-dev
FROM node:${NODE_VERSION}-alpine AS builder

RUN apk add --no-cache python3 make g++

WORKDIR /app

COPY package*.json ./
RUN npm ci
# Cypress runs on the host against the container, never inside it: skip the
# binary its postinstall would otherwise download.
RUN CYPRESS_INSTALL_BINARY=0 npm ci && npm cache clean --force

COPY . .

Expand All @@ -22,7 +25,17 @@ RUN DATABASE_URL=file:/tmp/build.db npx prisma db push --skip-generate
ENV NEXT_TELEMETRY_DISABLED=1
RUN DATABASE_URL=file:/tmp/build.db npm run build

RUN rm -f /tmp/build.db
# The entrypoint seeds through tsx, and both the seed and the Prisma config
# load dotenv: they are runtime dependencies, so the prune must keep them.
RUN npm prune --omit=dev \
&& rm -rf .next/cache /tmp/build.db \
&& chmod +x docker-entrypoint.sh

FROM node:${NODE_VERSION}-alpine

WORKDIR /app

COPY --from=builder /app /app

RUN mkdir -p /app/data /app/uploads /app/documents/invoices /app/logs

Expand All @@ -32,7 +45,5 @@ ENV DATABASE_URL=file:/app/data/dev.db
ENV PARTNER_SIGNING_KEY_PATH=/app/data/partner-signing-key.pem
ENV NEXT_PUBLIC_BASE_URL=http://localhost:3000

RUN chmod +x ./docker-entrypoint.sh

EXPOSE 3000
ENTRYPOINT ["./docker-entrypoint.sh"]
5 changes: 3 additions & 2 deletions docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ DB_FILE="${DATABASE_URL#file:}"

if [ ! -f "$DB_FILE" ]; then
echo "First run: initializing database..."
npx prisma db push --skip-generate
npx tsx prisma/seed.ts
# Without a TTY, npx would download a missing package instead of failing.
npx --no prisma db push --skip-generate
npx --no tsx prisma/seed.ts
echo "Database initialized successfully."
else
# A pulled image brings models and challenges the mounted volume never saw.
Expand Down
Loading
Loading