Skip to content

Commit 62f2aaf

Browse files
committed
feat: add docker support
1 parent 1b96c33 commit 62f2aaf

16 files changed

Lines changed: 420 additions & 43 deletions

File tree

.dockerignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Ignore unnecessary files for Docker context
2+
node_modules
3+
.next/cache
4+
.next/types
5+
dist
6+
coverage
7+
tests
8+
*.log
9+
.env*
10+
.git
11+
.gitignore
12+
Dockerfile
13+
docker-compose.yml
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: release-replane-image
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
tags: [ 'v*.*.*' ]
7+
8+
permissions:
9+
contents: read
10+
packages: write
11+
12+
env:
13+
REGISTRY: ghcr.io
14+
IMAGE_NAME: ${{ github.repository_owner }}/replane
15+
16+
jobs:
17+
build-and-push:
18+
runs-on: ubuntu-latest
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v4
22+
23+
- name: Set up QEMU (multi-arch)
24+
uses: docker/setup-qemu-action@v3
25+
26+
- name: Set up Buildx
27+
uses: docker/setup-buildx-action@v3
28+
29+
- name: Log in to GHCR
30+
uses: docker/login-action@v3
31+
with:
32+
registry: ${{ env.REGISTRY }}
33+
username: ${{ github.actor }}
34+
password: ${{ secrets.GITHUB_TOKEN }}
35+
36+
- name: Extract Docker metadata
37+
id: meta
38+
uses: docker/metadata-action@v5
39+
with:
40+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
41+
tags: |
42+
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
43+
type=raw,value=edge,enable=${{ github.ref == 'refs/heads/main' }}
44+
type=ref,event=tag
45+
type=sha,format=short
46+
47+
- name: Enable corepack
48+
run: corepack enable
49+
50+
- name: Build and push image
51+
uses: docker/build-push-action@v6
52+
with:
53+
context: ./replane
54+
file: ./replane/Dockerfile
55+
push: true
56+
platforms: linux/amd64,linux/arm64
57+
cache-from: type=gha
58+
cache-to: type=gha,mode=max
59+
tags: ${{ steps.meta.outputs.tags }}
60+
labels: ${{ steps.meta.outputs.labels }}
61+
build-args: |
62+
NEXT_PUBLIC_BUILD_SHA=${{ github.sha }}
63+
64+
- name: Summary
65+
run: |
66+
echo "Published image tags:" >> $GITHUB_STEP_SUMMARY
67+
echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY

Dockerfile

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
###########
2+
# Replane Production Dockerfile
3+
# Multi-stage build for a Next.js 15 app using pnpm.
4+
# Supports multi-arch when built via buildx (linux/amd64, linux/arm64).
5+
###########
6+
7+
# 1) Base image
8+
FROM node:22-alpine AS base
9+
WORKDIR /app
10+
ENV NODE_ENV=production \
11+
NEXT_TELEMETRY_DISABLED=1
12+
13+
# 2) Dependencies (install all deps including dev for build)
14+
FROM base AS deps
15+
RUN corepack enable
16+
COPY package.json pnpm-lock.yaml ./
17+
RUN pnpm install --frozen-lockfile
18+
19+
# 3) Build
20+
FROM base AS build
21+
RUN corepack enable
22+
ARG NEXT_PUBLIC_BUILD_SHA
23+
ENV NEXT_PUBLIC_BUILD_SHA=$NEXT_PUBLIC_BUILD_SHA
24+
COPY package.json pnpm-lock.yaml ./
25+
COPY --from=deps /app/node_modules ./node_modules
26+
COPY . .
27+
RUN pnpm build
28+
29+
# 4) Prune to production dependencies only
30+
FROM base AS prune
31+
RUN corepack enable
32+
COPY package.json pnpm-lock.yaml ./
33+
COPY --from=build /app/node_modules ./node_modules
34+
RUN pnpm prune --prod
35+
36+
# 5) Runtime image
37+
FROM node:22-alpine AS runner
38+
WORKDIR /app
39+
ENV NODE_ENV=production \
40+
NEXT_TELEMETRY_DISABLED=1
41+
ARG NEXT_PUBLIC_BUILD_SHA
42+
ENV NEXT_PUBLIC_BUILD_SHA=$NEXT_PUBLIC_BUILD_SHA
43+
44+
# Create non-root user
45+
RUN addgroup -g 1001 nodejs && adduser -D -u 1001 nextjs -G nodejs
46+
47+
# Copy production node_modules and build artifacts
48+
COPY --from=prune /app/node_modules ./node_modules
49+
COPY --from=build /app/.next ./.next
50+
COPY --from=build /app/public ./public
51+
COPY --from=build /app/package.json ./package.json
52+
COPY --from=build /app/next.config.ts ./next.config.ts
53+
54+
USER nextjs
55+
EXPOSE 3000
56+
57+
# Basic healthcheck (adjust if you change the endpoint)
58+
HEALTHCHECK --interval=30s --timeout=5s --retries=5 CMD wget -qO- http://127.0.0.1:3000/api/health || exit 1
59+
60+
CMD ["node", "node_modules/next/dist/bin/next", "start"]

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,45 @@ This is pre‑1.0 software. Data model & endpoints may change. Don’t put missi
119119
## License
120120

121121
MIT
122+
123+
## Docker Image
124+
125+
A multi-arch (amd64/arm64) image is published to GitHub Container Registry on every push to `main` and on version tags (`v*.*.*`).
126+
127+
Pull the latest image:
128+
129+
```bash
130+
# authenticate (if private)
131+
echo $GITHUB_TOKEN | docker login ghcr.io -u <your-username> --password-stdin
132+
133+
# pull
134+
docker pull ghcr.io/<org-or-user>/replane:latest
135+
```
136+
137+
Run locally (remember to provide required env vars):
138+
139+
```bash
140+
docker run --rm -p 3000:3000 \
141+
-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/replane \
142+
-e NEXTAUTH_URL=http://localhost:3000 \
143+
-e NEXTAUTH_SECRET=changeme \
144+
ghcr.io/<org-or-user>/replane:latest
145+
```
146+
147+
Health endpoint: `GET /api/health` returns `{ "status": "ok" }`.
148+
149+
Build locally for testing:
150+
151+
```bash
152+
docker build -t replane:local ./replane
153+
```
154+
155+
To release a versioned tag (publishes `vX.Y.Z` + `latest`):
156+
157+
```bash
158+
git tag v0.2.0 && git push origin v0.2.0
159+
```
160+
161+
---
162+
163+
> Build arg: `NEXT_PUBLIC_BUILD_SHA` is injected automatically in CI.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"lint": "next lint",
1010
"test": "dotenv -- vitest run",
1111
"test:watch": "dotenv -- vitest",
12+
"release": "bumpp",
1213
"migrate": "pnpm run migrate:migrate && pnpm run migrate:codegen",
1314
"migrate:migrate": "dotenv -- tsx ./src/engine/scripts/migrate.ts",
1415
"migrate:codegen": "dotenv -- kysely-codegen --out-file ./src/engine/core/db.d.ts && prettier --write ./src/engine/core/db.d.ts && eslint ./src/engine/core/db.d.ts --fix"
@@ -94,6 +95,7 @@
9495
"@types/react": "^19",
9596
"@types/react-dom": "^19",
9697
"@vitejs/plugin-react": "^5.0.1",
98+
"bumpp": "^10.2.3",
9799
"dotenv-cli": "^10.0.0",
98100
"editorconfig": "^3.0.1",
99101
"eslint": "^9",

0 commit comments

Comments
 (0)