Skip to content

Commit 8232f2a

Browse files
committed
feat: add Docker integration for workers and Redis setup
- Introduced a multi-stage Dockerfile for the workers app with dependency installation, build, and runtime layers. - Added GitHub Actions workflow to build and push worker Docker images, supporting multiple architectures (amd64, arm64). - Updated `docker-compose.test.yml` to include worker and Redis services for local testing. - Expanded `apps/workers/package.json` dependencies with logging (`pino`, `pino-pretty`), Prisma client, and PostgreSQL (`pg`). - Adjusted `pnpm-lock.yaml` to reflect new dependencies.
1 parent 1b023fc commit 8232f2a

5 files changed

Lines changed: 270 additions & 0 deletions

File tree

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
name: Build and Push Docker Image
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- 'apps/web/package.json'
9+
10+
env:
11+
REGISTRY: ghcr.io
12+
IMAGE_NAME: seastackapp/seastack-worker
13+
14+
jobs:
15+
check-version-change:
16+
runs-on: ubuntu-latest
17+
permissions:
18+
contents: read
19+
outputs:
20+
version_changed: ${{ steps.check.outputs.changed }}
21+
new_version: ${{ steps.get_version.outputs.version }}
22+
steps:
23+
- name: Checkout code
24+
uses: actions/checkout@v4.2.2
25+
with:
26+
fetch-depth: 2
27+
28+
- name: Get current version
29+
id: get_version
30+
run: |
31+
VERSION=$(jq -r '.version' apps/web/package.json)
32+
echo "version=$VERSION" >> $GITHUB_OUTPUT
33+
echo "Current version: $VERSION"
34+
35+
- name: Get previous version
36+
id: get_prev_version
37+
run: |
38+
if git show HEAD^1:apps/web/package.json > /dev/null 2>&1; then
39+
PREV_VERSION=$(git show HEAD^1:apps/web/package.json | jq -r '.version')
40+
else
41+
PREV_VERSION="none"
42+
fi
43+
echo "prev_version=$PREV_VERSION" >> $GITHUB_OUTPUT
44+
echo "Previous version: $PREV_VERSION"
45+
46+
- name: Check if version changed
47+
id: check
48+
run: |
49+
CURRENT="${{ steps.get_version.outputs.version }}"
50+
PREVIOUS="${{ steps.get_prev_version.outputs.prev_version }}"
51+
if [ "$CURRENT" != "$PREVIOUS" ]; then
52+
echo "changed=true" >> $GITHUB_OUTPUT
53+
echo "Version changed from $PREVIOUS to $CURRENT"
54+
else
55+
echo "changed=false" >> $GITHUB_OUTPUT
56+
echo "Version did not change"
57+
fi
58+
59+
build-and-push:
60+
needs: check-version-change
61+
if: needs.check-version-change.outputs.version_changed == 'true'
62+
strategy:
63+
matrix:
64+
include:
65+
- runner: ubuntu-latest
66+
platform: linux/amd64
67+
- runner: ubuntu-24.04-arm
68+
platform: linux/arm64
69+
runs-on: ${{ matrix.runner }}
70+
permissions:
71+
contents: read
72+
packages: write
73+
steps:
74+
- name: Checkout code
75+
uses: actions/checkout@v4.2.2
76+
77+
- name: Set up Docker Buildx
78+
uses: docker/setup-buildx-action@v3.7.1
79+
80+
- name: Log in to GitHub Container Registry
81+
uses: docker/login-action@v3.3.0
82+
with:
83+
registry: ghcr.io
84+
username: ${{ github.actor }}
85+
password: ${{ secrets.GITHUB_TOKEN }}
86+
87+
- name: Build and push by digest
88+
id: build
89+
uses: docker/build-push-action@v6.10.0
90+
with:
91+
context: .
92+
file: ./apps/workers/Dockerfile
93+
platforms: ${{ matrix.platform }}
94+
outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
95+
cache-from: type=gha
96+
cache-to: type=gha,mode=max
97+
98+
- name: Export digest
99+
run: |
100+
mkdir -p /tmp/digests
101+
digest="${{ steps.build.outputs.digest }}"
102+
touch "/tmp/digests/${digest#sha256:}"
103+
104+
- name: Upload digest
105+
uses: actions/upload-artifact@v4
106+
with:
107+
name: digests-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }}
108+
path: /tmp/digests/*
109+
if-no-files-found: error
110+
retention-days: 1
111+
112+
merge:
113+
needs: [check-version-change, build-and-push]
114+
runs-on: ubuntu-latest
115+
permissions:
116+
contents: read
117+
packages: write
118+
steps:
119+
- name: Download digests
120+
uses: actions/download-artifact@v4
121+
with:
122+
path: /tmp/digests
123+
pattern: digests-*
124+
merge-multiple: true
125+
126+
- name: Set up Docker Buildx
127+
uses: docker/setup-buildx-action@v3.7.1
128+
129+
- name: Log in to GitHub Container Registry
130+
uses: docker/login-action@v3.3.0
131+
with:
132+
registry: ghcr.io
133+
username: ${{ github.actor }}
134+
password: ${{ secrets.GITHUB_TOKEN }}
135+
136+
- name: Extract metadata
137+
id: meta
138+
uses: docker/metadata-action@v5.6.1
139+
with:
140+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
141+
tags: |
142+
type=raw,value=latest
143+
type=raw,value=${{ needs.check-version-change.outputs.new_version }}
144+
145+
- name: Create manifest list and push
146+
working-directory: /tmp/digests
147+
run: |
148+
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
149+
$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)

apps/workers/Dockerfile

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Multi-stage Dockerfile for the workers app in a pnpm + turbo monorepo
2+
# Uses Alpine for the final worker image
3+
4+
# -----------------------
5+
# 1) Base image with pnpm
6+
# -----------------------
7+
FROM node:24-alpine AS base
8+
9+
# Runtime dependencies commonly needed by Node native modules & Prisma
10+
RUN apk add --no-cache libc6-compat openssl
11+
12+
# Enable corepack and pin pnpm (must match repo tooling)
13+
RUN corepack enable && corepack prepare pnpm@9.0.0 --activate
14+
15+
WORKDIR /app
16+
17+
# -----------------------
18+
# 2) Dependencies layer
19+
# - install workspace deps with caching
20+
# -----------------------
21+
FROM base AS deps
22+
23+
# Build toolchain for native modules (not in final image)
24+
RUN apk add --no-cache python3 make g++
25+
26+
# Copy only files needed to resolve the dependency graph to leverage Docker cache
27+
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json turbo.json ./
28+
29+
# Copy workspace manifests used by the workers app and its internal deps
30+
COPY apps/workers/package.json ./apps/workers/package.json
31+
COPY packages ./packages
32+
33+
# Install dependencies (workspace-aware) using lockfile
34+
RUN pnpm install --frozen-lockfile
35+
36+
# -----------------------
37+
# 3) Build layer
38+
# -----------------------
39+
FROM deps AS build
40+
41+
# Bring in full source to build the workers package
42+
COPY . .
43+
44+
# Provide safe defaults for codegen steps that don't require a live DB
45+
ENV DATABASE_URL=postgres://postgres:password@postgres:5432/postgres
46+
47+
# Generate Prisma client for @repo/db (no live DB needed)
48+
RUN pnpm --filter @repo/db... generate || pnpm --filter @repo/db generate || true
49+
50+
# Build the workers app (outputs to apps/workers/dist)
51+
RUN pnpm --filter workers build
52+
53+
# Produce a pruned, production-ready output for just the workers package
54+
# This includes the package files, its dist, and a pruned node_modules
55+
RUN pnpm deploy --filter workers --prod /out
56+
57+
# -----------------------
58+
# 4) Runtime layer (Alpine)
59+
# -----------------------
60+
FROM node:24-alpine AS runner
61+
62+
ENV NODE_ENV=production
63+
64+
# Runtime libs needed by Prisma and other native deps
65+
RUN apk add --no-cache libc6-compat openssl
66+
67+
# Non-root user for security
68+
RUN adduser -D worker
69+
70+
WORKDIR /app
71+
72+
# Copy the pruned deployment from build stage
73+
COPY --from=build /out/ .
74+
75+
USER worker
76+
77+
# Start the worker process
78+
CMD ["node", "dist/index.mjs"]

apps/workers/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@
1414
"dependencies": {
1515
"@dotenvx/dotenvx": "^1.51.1",
1616
"@prisma/adapter-pg": "^7.1.0",
17+
"@prisma/client": "^7.1.0",
1718
"@repo/db": "workspace:*",
1819
"@repo/queues": "workspace:*",
1920
"@repo/utils": "workspace:*",
2021
"@types/ssh2": "^1.15.5",
2122
"bullmq": "^5.65.1",
2223
"ioredis": "^5.8.2",
24+
"pino": "^10.1.0",
25+
"pino-pretty": "^13.1.3",
26+
"pg": "^8.16.3",
2327
"prisma": "^7.1.0",
2428
"ssh2": "^1.17.0"
2529
},

docker-compose.test.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,33 @@ services:
2121
BETTER_AUTH_SECRET: CeOx3AmjqVWNIlmeMLOgwtq87J49YpQX
2222
BETTER_AUTH_URL: http://localhost:3000
2323
ENCRYPTION_SECRET: CeOx3AmjqVWNIlmeMLOgwtq87J49YpQX
24+
REDIS_HOST: redis
25+
REDIS_PORT: 6379
26+
REDIS_PASSWORD: devpassword
27+
28+
worker:
29+
image: ghcr.io/seastackapp/seastack-worker:latest
30+
depends_on:
31+
- postgres
32+
- redis
33+
environment:
34+
DATABASE_URL: "postgresql://postgres:password@postgres:5432/public?schema=public"
35+
BETTER_AUTH_SECRET: CeOx3AmjqVWNIlmeMLOgwtq87J49YpQX
36+
BETTER_AUTH_URL: http://localhost:3000
37+
ENCRYPTION_SECRET: CeOx3AmjqVWNIlmeMLOgwtq87J49YpQX
38+
REDIS_HOST: redis
39+
REDIS_PORT: 6379
40+
REDIS_PASSWORD: devpassword
41+
redis:
42+
image: redis:7.4-alpine
43+
command: redis-server --requirepass devpassword
44+
environment:
45+
REDIS_PASSWORD: devpassword
46+
ports:
47+
- "6379:6379"
48+
volumes:
49+
- redis_data:/data
2450

2551
volumes:
2652
postgres_data:
53+
redis_data:

pnpm-lock.yaml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)