Tap is a proof-of-concept editor for screenplays formatted in Fountain markup. It stores all data in AT Protocol records.
1# Multi-stage build: build web bundle with Node, then build Go server, then produce a small runtime image
2
3# 1) Build Web Components bundle (esbuild)
4FROM node:20-alpine AS webbuild
5WORKDIR /app
6
7# Copy only what we need for the web build first (faster caching)
8COPY package.json package-lock.json ./
9COPY web ./web
10# Ensure output directory exists (esbuild writes to server/static/js/tap.js)
11RUN mkdir -p server/static/js
12
13RUN npm ci && npm run build:wc
14
15# 2) Build Go server
16FROM golang:1.24-alpine AS gobuild
17WORKDIR /app/server
18
19# Copy Go module and source
20COPY server/go.mod ./go.mod
21COPY server/go.sum ./go.sum
22# Pre-fetch modules for better caching
23RUN go mod download
24
25# Now copy the rest of the source (entire server tree)
26COPY server/ ./
27
28# Bring in built web assets from the previous stage (overwrites js bundle)
29COPY --from=webbuild /app/server/static/js /app/server/static/js
30
31ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64
32RUN go build -o /app/server/app .
33
34# 3) Runtime image
35FROM alpine:3.19 AS runtime
36RUN apk add --no-cache ca-certificates
37WORKDIR /app/server
38
39# Copy binary and runtime assets
40COPY --from=gobuild /app/server/app ./app
41COPY --from=gobuild /app/server/templates ./templates
42COPY --from=gobuild /app/server/static ./static
43
44# Fly will set PORT; our server defaults to 8088 if not set
45ENV PORT=8088
46EXPOSE 8088
47
48# Security best-practice: run as non-root
49RUN addgroup -S app && adduser -S app -G app
50USER app
51
52CMD ["./app"]