Repository navigation
Expand file tree
/
Copy pathDockerfile
More file actions
236 lines (190 loc) · 9.22 KB
/
Copy pathDockerfile
File metadata and controls
236 lines (190 loc) · 9.22 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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# Stage 1: Use official cargo-chef image as base.
# RUST_VERSION is the single source of truth for the build toolchain; keep it in
# lockstep with .github/workflows/ci.yml (env.RUST_VERSION) and mise.toml.
ARG RUST_VERSION=1.97
FROM lukemathwalker/cargo-chef:latest-rust-${RUST_VERSION} AS chef
WORKDIR /usr/src
# Install build dependencies for Debian (including perl and make for vendored OpenSSL build)
RUN apt-get update && apt-get install -y \
pkg-config \
libssl-dev \
perl \
make \
&& rm -rf /var/lib/apt/lists/*
# Stage 2: Generate recipe file for dependencies
FROM chef AS planner
# Copy the whole workspace, not a hand-listed set of manifests.
#
# `cargo chef prepare` reads every workspace member's Cargo.toml, so a list has
# to name all of them — and a crate added without a matching line here fails the
# build on a missing manifest, far from the change that caused it. Copying
# sources into this stage costs nothing downstream: its only output is
# recipe.json, and the expensive `cargo chef cook` layer in the builder is keyed
# on that file's content, which changes only when dependencies change.
COPY Cargo.toml Cargo.lock ./
COPY crates ./crates
# Cargo wants a source file for the root binary; the recipe needs only manifests.
RUN mkdir -p src && echo "fn main() {}" > src/main.rs
RUN cargo chef prepare --recipe-path recipe.json
# Stage 2.5: Build frontend assets
FROM node:26-alpine AS frontend-builder
WORKDIR /usr/src/frontend
COPY frontend/package.json frontend/package-lock.json ./
RUN npm ci
COPY frontend/ ./
COPY static/assets/logo.svg ../static/assets/logo.svg
RUN npm run build
# Stage 2.6: Build bundled user documentation
FROM node:26-alpine AS user-docs-builder
WORKDIR /usr/src/docs
COPY docs/package.json docs/package-lock.json ./
COPY docs/user/package.json ./user/
COPY docs/engineering/package.json ./engineering/
RUN npm ci
COPY docs/ ./
COPY skills/rise-app-builder /usr/src/skills/rise-app-builder
RUN RISE_USER_DOCS_URL=/docs node scripts/generate-llms.mjs && \
npm run build --workspace=rise-user-docs
# Stage 3: Build dependencies (cached separately from source code)
FROM chef AS builder
COPY --from=planner /usr/src/recipe.json recipe.json
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \
--mount=type=cache,target=/usr/src/target,sharing=locked \
cargo chef cook --release --all-features --recipe-path recipe.json
# Copy project files
COPY Cargo.toml Cargo.lock ./
COPY crates ./crates
COPY src ./src
COPY migrations ./migrations
COPY static ./static
COPY --from=frontend-builder /usr/src/frontend/dist/ ./static/
COPY .sqlx ./.sqlx
COPY scripts/refresh-cargo-source-mtimes.sh ./scripts/refresh-cargo-source-mtimes.sh
# Build the application with server features
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \
--mount=type=cache,target=/usr/src/target,sharing=locked \
scripts/refresh-cargo-source-mtimes.sh && \
SQLX_OFFLINE=true cargo build --release --all-features --bin rise && \
cp target/release/rise /usr/local/bin/rise
# Stage 3b: Build the CLI-only binary for the rise-cli image. Its own target
# cache: a different feature set must not churn the server build's artifacts.
FROM chef AS cli-builder
COPY --from=planner /usr/src/recipe.json recipe.json
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \
--mount=type=cache,id=rise-cli-target,target=/usr/src/target,sharing=locked \
cargo chef cook --release --no-default-features --features cli --recipe-path recipe.json
COPY Cargo.toml Cargo.lock ./
COPY crates ./crates
COPY src ./src
COPY migrations ./migrations
COPY static ./static
COPY .sqlx ./.sqlx
COPY scripts/refresh-cargo-source-mtimes.sh ./scripts/refresh-cargo-source-mtimes.sh
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \
--mount=type=cache,id=rise-cli-target,target=/usr/src/target,sharing=locked \
scripts/refresh-cargo-source-mtimes.sh && \
SQLX_OFFLINE=true cargo build --release --no-default-features --features cli --bin rise && \
cp target/release/rise /usr/local/bin/rise
# Stage 4: Create the final, smaller image (match builder's Debian version)
FROM debian:trixie-slim AS rise
# Runtime dependencies. Deliberately no HTTP client: the ECS health check runs
# `rise backend health`, which probes the server over loopback using the binary
# that is already here, so the image needs no curl or wget for it.
RUN apt-get update && apt-get install -y \
ca-certificates \
libssl3 \
&& rm -rf /var/lib/apt/lists/*
# Copy the compiled binary from the builder stage
COPY --from=builder /usr/local/bin/rise /usr/local/bin/rise
# The server binary links the BUSL-1.1 Kubernetes and ECS backends alongside the
# MIT OR Apache-2.0 core, so the image carries all three license texts.
COPY LICENSE-MIT LICENSE-APACHE LICENSE-BSL LICENSING.md /usr/share/doc/rise/
LABEL org.opencontainers.image.licenses="(MIT OR Apache-2.0) AND BUSL-1.1"
# Copy the configuration files
COPY config /etc/rise
# Copy static assets for filesystem-based serving (templates, SVGs, Vite build output)
COPY --from=builder /usr/src/static /var/lib/rise/static
# Copy built user documentation for serving via docs_dir
COPY --from=user-docs-builder /usr/src/docs/user/dist /var/rise/docs
# Default config location/run mode for containerized execution
ENV RISE_CONFIG_DIR=/etc/rise
ENV RISE_CONFIG_RUN_MODE=production
ENV RISE_STATIC_DIR=/var/lib/rise/static
ENV RISE_DOCS_DIR=/var/rise/docs
# Expose the application port
EXPOSE 3000
# Set the entrypoint
ENTRYPOINT ["/usr/local/bin/rise"]
# Stage 4b: The CLI alone, as small as it can be. It exists for the ECS
# workload-identity sidecar (`rise identity agent`), which Fargate pulls on every
# task start with no image cache, and for running `rise` in a workload. The CLI
# links only glibc, so distroless/cc suffices: no shell, no package manager. It
# runs as root because it writes to a task volume ECS creates root-owned.
FROM gcr.io/distroless/cc-debian13 AS rise-cli
COPY --from=cli-builder /usr/local/bin/rise /usr/local/bin/rise
# The CLI links no BSL code.
COPY LICENSE-MIT LICENSE-APACHE /usr/share/doc/rise/
LABEL org.opencontainers.image.licenses="MIT OR Apache-2.0"
ENTRYPOINT ["/usr/local/bin/rise"]
# Stage 5: Create the builder image with additional build tools
# Start from debian instead of rise to improve layer caching
FROM debian:trixie-slim AS rise-builder
# Install runtime dependencies (same as rise stage)
RUN apt-get update && apt-get install -y \
ca-certificates \
libssl3 \
&& rm -rf /var/lib/apt/lists/*
# Install dependencies for mise and build tools
RUN apt-get update && apt-get install -y \
curl \
git \
&& rm -rf /var/lib/apt/lists/*
# Install mise
RUN curl https://mise.run | sh
# Set up mise environment
ENV PATH="/root/.local/bin:/root/.local/share/mise/shims:${PATH}"
# Install build tools via mise.
#
# Pin every tool version explicitly — `@latest` here is what broke the image
# build: a Docker CLI plugin-loading regression in the 29.1.x line makes
# `docker buildx version` (and `docker trust`) report "unknown command: docker
# buildx" even though the plugin binary is present in the searched
# `~/.docker/cli-plugins` directory. See https://github.com/docker/cli/issues/6733.
# pack/railpack/buildkit mirror the versions pinned in `mise.toml`; docker-cli is
# held at the last 29.0.x release (29.0.4), the newest line before the regression
# — buildx itself is fine, so it's pinned to the current latest (0.34.1). Bump
# docker-cli back to the 29.1+ line once the upstream issue is resolved.
ARG PACK_VERSION=0.40.6
ARG DOCKER_CLI_VERSION=29.0.4
ARG RAILPACK_VERSION=0.15.1
ARG BUILDX_VERSION=0.34.1
ARG BUILDKIT_VERSION=0.28.0
ARG TARGETARCH
RUN /root/.local/bin/mise use -g pack@${PACK_VERSION} && \
/root/.local/bin/mise use -g docker-cli@${DOCKER_CLI_VERSION} && \
/root/.local/bin/mise use -g ubi:railwayapp/railpack@${RAILPACK_VERSION} && \
/root/.local/bin/mise install
# Install Docker buildx plugin manually (pinned).
RUN mkdir -p /root/.docker/cli-plugins && \
curl -sSL "https://github.com/docker/buildx/releases/download/v${BUILDX_VERSION}/buildx-v${BUILDX_VERSION}.linux-${TARGETARCH}" -o /root/.docker/cli-plugins/docker-buildx && \
chmod +x /root/.docker/cli-plugins/docker-buildx
# Install buildctl from buildkit (pinned).
RUN curl -sSL "https://github.com/moby/buildkit/releases/download/v${BUILDKIT_VERSION}/buildkit-v${BUILDKIT_VERSION}.linux-${TARGETARCH}.tar.gz" | tar -xz -C /usr/local bin/buildctl && \
chmod +x /usr/local/bin/buildctl
# Verify installations
RUN /root/.local/bin/mise exec -- pack version && \
/root/.local/bin/mise exec -- docker --version && \
/root/.local/bin/mise exec -- docker buildx version && \
buildctl --version
# Copy the rise CLI binary (last to maximize layer caching)
COPY --from=builder /usr/local/bin/rise /usr/local/bin/rise
# Same server binary as the `rise` stage, so the same three license texts.
COPY LICENSE-MIT LICENSE-APACHE LICENSE-BSL LICENSING.md /usr/share/doc/rise/
LABEL org.opencontainers.image.licenses="(MIT OR Apache-2.0) AND BUSL-1.1"
# Set the entrypoint
ENTRYPOINT []
CMD ["/usr/bin/bash"]