-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathDockerfile
More file actions
154 lines (119 loc) · 5.36 KB
/
Copy pathDockerfile
File metadata and controls
154 lines (119 loc) · 5.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# ===================================================
# TuneCamp Docker Image
# Multi-stage build for production deployment
# ===================================================
# Top-level ARGs for build time
ARG TUNECAMP_RPC_URL
ARG TUNECAMP_CURRENCY_CONTRACT
# CapRover passes this on deploy; using it invalidates cache per commit
ARG CAPROVER_GIT_COMMIT_SHA
# Build stage
FROM node:22-alpine AS builder
# Re-declare ARGs needed in this stage (multi-stage build)
ARG CAPROVER_GIT_COMMIT_SHA
ARG TUNECAMP_RPC_URL
ARG TUNECAMP_CURRENCY_CONTRACT
WORKDIR /app
# Prevent Puppeteer from downloading Chromium during build time
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
# Consume build-args (avoids unconsumed build-arg warnings; SHA also busts cache per deploy)
RUN echo "CapRover commit: ${CAPROVER_GIT_COMMIT_SHA:-none}"
# Install build dependencies for native modules (better-sqlite3)
RUN apk add --no-cache python3 make g++ curl git libc6-compat gcompat unzip
# Copy package files and local dependencies
COPY package*.json ./
COPY scripts ./scripts
COPY webapp/package.json ./webapp/
# Install all dependencies (including dev) for the entire workspace.
# rollup/lightningcss/oxide ship their musl binaries as per-arch packages that
# are not all in the lockfile, so they are installed explicitly. The arch comes
# from `uname -m` inside the build, not from TARGETARCH: a default on a
# predefined ARG silently stays `amd64` under the classic builder, which then
# installs x64 binaries on an Apple Silicon Mac and fails with EBADPLATFORM.
RUN if [ "$(uname -m)" = "aarch64" ]; then \
ROLLUP_PKG="@rollup/rollup-linux-arm64-musl" && \
LIGHT_CSS_PKG="lightningcss-linux-arm64-musl" && \
OXIDE_PKG="@tailwindcss/oxide-linux-arm64-musl"; \
else \
ROLLUP_PKG="@rollup/rollup-linux-x64-musl" && \
LIGHT_CSS_PKG="lightningcss-linux-x64-musl" && \
OXIDE_PKG="@tailwindcss/oxide-linux-x64-musl"; \
fi && \
npm ci && \
npm install $ROLLUP_PKG $LIGHT_CSS_PKG $OXIDE_PKG && \
npm cache clean --force && \
rm -rf /root/.npm/_cacache
# Copy source code
COPY . .
# Build TypeScript (Server)
RUN npm run build
# Pass ARGs to VITE_ ENVs for frontend build
ENV VITE_TUNECAMP_RPC_URL=$TUNECAMP_RPC_URL
ENV VITE_TUNECAMP_CURRENCY_CONTRACT=$TUNECAMP_CURRENCY_CONTRACT
# Build Frontend (using workspace command)
RUN npm run build -w webapp
# Ensure all public assets (manifest, sw, icons) are in dist
RUN cp -v webapp/public/manifest.json webapp/dist/ 2>/dev/null || true
RUN cp -v webapp/public/sw.js webapp/dist/ 2>/dev/null || true
RUN cp -rv webapp/public/* webapp/dist/ 2>/dev/null || true
# ===================================================
# Production stage
# ===================================================
FROM node:22-alpine
# Re-declare ARG so production stage gets fresh value; busts cache so new code is always copied
ARG CAPROVER_GIT_COMMIT_SHA
WORKDIR /app
# Cache buster: forces this stage to rebuild every deploy (no "Using cache" on COPY --from=builder)
RUN echo "Production deploy commit: ${CAPROVER_GIT_COMMIT_SHA:-none}"
# Install runtime dependencies for native modules
RUN apk add --no-cache \
ffmpeg \
curl \
python3 \
libc6-compat \
gcompat
# Copy package files, local dependencies and install production dependencies
COPY package*.json ./
COPY scripts ./scripts
COPY webapp/package.json ./webapp/
# Install build tools, run npm ci, and cleanup in one layer
RUN apk add --no-cache --virtual .build-deps python3 make g++ git && \
npm ci --omit=dev && \
npm cache clean --force && \
apk del .build-deps && \
rm -rf /root/.npm /tmp/*
# Copy built files from builder
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/webapp/dist ./webapp/dist
COPY --from=builder /app/webapp/public ./webapp/public
COPY --from=builder /app/CHANGELOG.md ./CHANGELOG.md
# Create directories for data persistence
RUN mkdir -p /music /data /radata
# Re-declare ARG for production stage
ARG CAPROVER_GIT_COMMIT_SHA
# Environment variables (runtime defaults)
# Security Note: Sensitive credentials, API keys, and secrets (e.g. STRIPE_SECRET_KEY,
# TUNECAMP_ADMIN_PASS, OPENROUTER_API_KEY, TELEGRAM_BOT_TOKEN, etc.) MUST NOT be
# passed as Docker build ARGs to prevent leaking in image metadata/history.
# They are loaded securely at runtime via container environment variables
# (e.g. docker-compose.yml env_file, docker run -e, or CapRover App Config).
ENV NODE_ENV=production
# Deploy commit, used by Sentry as release tag (set SENTRY_DSN to enable crash reporting)
ENV TUNECAMP_GIT_SHA=$CAPROVER_GIT_COMMIT_SHA
ENV TUNECAMP_DB_PATH=/data/tunecamp.db
ENV TUNECAMP_MUSIC_DIR=/music
ENV SKIP_STARTUP_MAINTENANCE=true
# Puppeteer & Chromium configuration for Alpine
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
# Expose default port
EXPOSE 1970
# Install runtime dependencies
RUN apk add --no-cache chromium nss freetype harfbuzz ca-certificates ttf-freefont curl libc6-compat gcompat ffmpeg unzip python3 py3-pip && \
python3 -m pip install --break-system-packages -U yt-dlp bgutil-ytdlp-pot-provider
# Add a more lenient healthcheck
# to avoid restart loops during heavy maintenance/discovery
HEALTHCHECK --interval=60s --timeout=30s --start-period=180s --retries=5 \
CMD curl -f http://127.0.0.1:1970/health || exit 1
# Default command: start server directly
CMD ["node", "--expose-gc", "dist/index.js"]