-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathDockerfile
More file actions
149 lines (115 loc) · 5.43 KB
/
Dockerfile
File metadata and controls
149 lines (115 loc) · 5.43 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
ARG DOCKER_NAMESPACE=omnectweucopsacr.azurecr.io
ARG BUILD_IMAGE=${DOCKER_NAMESPACE}/rust:bookworm
ARG DISTROLESS_IMAGE=gcr.io/distroless/base-debian12:nonroot
FROM ${DISTROLESS_IMAGE} AS distroless
# Stage 1: Generate TypeScript types on host architecture (fast, no emulation)
FROM --platform=$BUILDPLATFORM ${BUILD_IMAGE} AS typegen
WORKDIR "/work"
COPY Cargo.lock Cargo.toml ./
COPY src ./src
# Build shared_types to generate TypeScript bindings (runs on host arch, fast)
# Note: pnpm is required by crux_core TypeGen, but we use a dummy to avoid hangs
RUN mkdir -p /tmp/bin && \
echo '#!/bin/sh' > /tmp/bin/pnpm && \
echo 'exit 0' >> /tmp/bin/pnpm && \
chmod +x /tmp/bin/pnpm && \
PATH="/tmp/bin:$PATH" cargo build --release -p shared_types
# Stage 2: Build Crux WASM core (parallel with typegen)
FROM --platform=$BUILDPLATFORM ${BUILD_IMAGE} AS wasm-build
WORKDIR /work
COPY Cargo.lock Cargo.toml ./
COPY src ./src
# Build WASM module for Crux core
# Note: wasm-pack is already in BUILD_IMAGE
RUN cd src/app && wasm-pack build --target web --out-dir ../ui/src/core/pkg
# Stage 3: Install Vue dependencies (parallel with typegen and wasm-build)
FROM --platform=$BUILDPLATFORM ${BUILD_IMAGE} AS vue-install
RUN mkdir -p /tmp
COPY src/ui/package.json /tmp
COPY src/ui/bun.lock /tmp
# Note: bun is already in BUILD_IMAGE
RUN cd /tmp && bun install --frozen-lockfile
# Stage 4: Build Vue UI using generated types and WASM from previous stages
FROM --platform=$BUILDPLATFORM ${BUILD_IMAGE} AS vue-build
WORKDIR /usr/src/app
COPY src/ui .
# Use TypeScript types generated by the typegen stage (built on host arch)
COPY --from=typegen /work/src/shared_types/generated/typescript ../shared_types/generated/typescript
# Copy WASM package from wasm-build stage
COPY --from=wasm-build /work/src/ui/src/core/pkg ./src/core/pkg
COPY --from=vue-install /tmp/node_modules node_modules
# Remove CommonJS .js files so Vite uses the TypeScript source directly
RUN find ../shared_types/generated/typescript -name "*.js" -delete
RUN bun run build
# Generate stripped package.json (runtime deps only) for SBOM generation
RUN echo 'const p = JSON.parse(await Bun.file("package.json").text()); delete p.devDependencies; delete p.scripts; await Bun.write("/tmp/sbom-package.json", JSON.stringify(p, null, 2));' | bun run -
# Stage 5: Build Rust backend for target architecture with embedded frontend
FROM ${DOCKER_NAMESPACE}/rust:bookworm AS builder
RUN apt-get update && \
apt-get install -y --no-install-recommends \
ca-certificates \
cmake \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
ARG TARGETARCH
ARG OMNECT_UI_BUILD_ARG=""
WORKDIR "/work"
COPY --from=distroless /var/lib/dpkg/status.d /distroless_pkgs
COPY Cargo.lock Cargo.toml ./
# Copy all workspace members (required by workspace to resolve dependencies)
COPY src/app ./src/app
COPY src/shared_types ./src/shared_types
COPY src/backend ./src/backend
# Copy built Vue frontend for embedding into the Rust binary
COPY --from=vue-build /usr/src/app/dist ./src/ui/dist
# Build omnect-ui for target architecture with embedded frontend
# Cache mounts persist compiled dependencies across builds
RUN --mount=type=cache,target=/usr/local/cargo/registry,id=cargo-registry-${TARGETARCH} \
--mount=type=cache,target=/work/build,id=cargo-build-${TARGETARCH} \
cargo auditable build ${OMNECT_UI_BUILD_ARG} --profile dist -p omnect-ui --target-dir ./build && \
cp ./build/dist/omnect-ui /work/omnect-ui-bin
SHELL ["/bin/bash", "-c"]
RUN <<EOT
set -eu
mkdir -p /copy/status.d
executable=(omnect-ui-bin)
mkdir -p /copy/$(dirname "${executable}")
cp "${executable}" /copy/"${executable}"
# gather libraries installed in distroless image to skip them
readarray -t FILTER < <(for file in $(find /distroless_pkgs -type f -! -name "*.md5sums"); do sed -n "s/Package: \(.*\)$/\1/p" $file; done)
# skip .so of the dynamic linker
LOADER=$(readelf -l "${executable}" | grep "interpreter:" | sed -e "s/.*interpreter: \(.*\)]$/\1/")
readarray -t LIBS < <(ldd "${executable}" | awk '{if ($3 == "") print $1; else print $3}')
for LIB in ${LIBS[@]}; do
# skip the linker loader
if [ "$LIB" == "$LOADER" ]; then
continue
fi
# the actual library location in the package may deviate from what the
# linker specifies, so update that info and gather the package name.
PKG_INFO=$(LOCALE=C.UTF-8 dpkg -S "*$LIB" 2> /dev/null) || continue
PKG="${PKG_INFO%%:*}"
LIB="${PKG_INFO##*: }"
# skip libraries already installed in distroless
if [[ " ${FILTER[*]} " =~ "${PKG} " ]]; then
continue
fi
# copy the library and its dpkg database entries
mkdir -p /copy/$(dirname "${LIB}")
cp "${LIB}" /copy/"${LIB}"
sed -n "/Package: ${PKG}/,/^$/p" /var/lib/dpkg/status > "/copy/status.d/${PKG}"
done
EOT
RUN mkdir /cert
# Final stage: Assemble the runtime image
FROM ${DISTROLESS_IMAGE} AS base
COPY --from=builder --chown=10000:10000 /cert /cert
COPY --from=builder /work/omnect-ui-bin /omnect-ui
COPY --from=builder /copy/lib/ /lib/
COPY --from=builder /copy/status.d /var/lib/dpkg/status.d
# npm runtime metadata for SBOM generation (stripped of devDependencies)
COPY --from=vue-build /tmp/sbom-package.json /sbom/npm/package.json
COPY --from=vue-build /usr/src/app/bun.lock /sbom/npm/bun.lock
WORKDIR "/"
ARG RUST_LOG="warn,omnect_ui=debug"
ENV RUST_LOG=${RUST_LOG}
ENTRYPOINT [ "/omnect-ui" ]