Files
Genarrative/deploy/container/gitea-ci-job.Dockerfile
kdletters 27de28a487 迁移 npm workspaces 统一依赖边界
根目录统一管理九个 workspace 与唯一 lockfile

迁移 CI、Jenkins 和容器的根目录单次 npm ci

补齐 hoist 兼容、版本校验与 workspace 门禁

同步依赖边界方案、运维文档和长期记忆
2026-08-22 13:17:26 +08:00

232 lines
11 KiB
Docker

ARG RUST_IMAGE=rust:1.96-bookworm@sha256:19817ead3289c8c631c73df281e18b59b172f6a31f4f563290f69cddd06c30e9
ARG RUNNER_IMAGE=docker.gitea.com/runner-images:ubuntu-latest@sha256:58ea92624c7c09582e05594d95488331045053d3a3f34cf09649f2a32313a614
FROM ${RUST_IMAGE} AS rust-toolchain
RUN rustup component add rustfmt \
&& rustc --version \
&& cargo --version \
&& rustfmt --version
FROM rust-toolchain AS rust-dependency-cache
ENV CARGO_HTTP_MULTIPLEXING=false \
CARGO_NET_RETRY=10
COPY server-rs /tmp/genarrative-cargo-cache/server-rs
COPY apps/desktop-shell/src-tauri /tmp/genarrative-cargo-cache/desktop-shell
COPY apps/ai-game-creator-shell/src-tauri /tmp/genarrative-cargo-cache/apps/ai-game-creator-shell/src-tauri
RUN find /tmp/genarrative-cargo-cache -name Cargo.toml -exec dirname {} \; \
| while IFS= read -r crate_dir; do \
mkdir -p "${crate_dir}/src"; \
: > "${crate_dir}/src/lib.rs"; \
: > "${crate_dir}/src/main.rs"; \
: > "${crate_dir}/src/active.rs"; \
done \
&& cargo_fetch_with_retry() { \
manifest_path="$1"; \
for attempt in 1 2 3 4 5; do \
if cargo fetch --locked \
--target x86_64-unknown-linux-gnu \
--manifest-path "${manifest_path}"; then \
return 0; \
fi; \
if [ "${attempt}" -eq 5 ]; then \
return 1; \
fi; \
sleep "$((attempt * 5))"; \
done; \
} \
&& cargo_fetch_with_retry /tmp/genarrative-cargo-cache/server-rs/Cargo.toml \
&& cargo_fetch_with_retry /tmp/genarrative-cargo-cache/desktop-shell/Cargo.toml \
&& cargo_fetch_with_retry /tmp/genarrative-cargo-cache/apps/ai-game-creator-shell/src-tauri/Cargo.toml \
&& CARGO_NET_OFFLINE=true cargo fetch --locked \
--target x86_64-unknown-linux-gnu \
--manifest-path /tmp/genarrative-cargo-cache/server-rs/Cargo.toml \
&& CARGO_NET_OFFLINE=true cargo fetch --locked \
--target x86_64-unknown-linux-gnu \
--manifest-path /tmp/genarrative-cargo-cache/desktop-shell/Cargo.toml \
&& CARGO_NET_OFFLINE=true cargo fetch --locked \
--target x86_64-unknown-linux-gnu \
--manifest-path /tmp/genarrative-cargo-cache/apps/ai-game-creator-shell/src-tauri/Cargo.toml \
&& rm -rf /tmp/genarrative-cargo-cache
FROM ${RUNNER_IMAGE}
ARG NODE_VERSION=22.23.1
ARG NODE_LINUX_X64_SHA256=9749e988f437343b7fa832c69ded82a312e41a03116d766797ac14f6f9eee578
ARG NPM_VERSION=10.9.7
ARG GOOGLE_CHROME_VERSION=150.0.7871.181-1
ARG GOOGLE_LINUX_SIGNING_KEY_FINGERPRINT=EB4C1BFD4F042F6DDDCCEC917721F63BD38B4796
LABEL org.opencontainers.image.title="Genarrative Gitea CI job image" \
org.opencontainers.image.description="Ubuntu 24.04 CI image with fixed toolchains and prewarmed npm/Cargo caches" \
org.opencontainers.image.version="2026.07.23.1"
RUN test "$(dpkg --print-architecture)" = "amd64" \
&& find /etc/apt/sources.list.d -maxdepth 1 -type f \
! -name ubuntu.sources -delete \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
bubblewrap \
build-essential \
ca-certificates \
clang \
cmake \
curl \
ffmpeg \
file \
libayatana-appindicator3-dev \
libclang-dev \
libcurl4-openssl-dev \
librsvg2-dev \
libssl-dev \
libwebkit2gtk-4.1-dev \
libxdo-dev \
lld \
patchelf \
pkg-config \
ripgrep \
wget \
xz-utils \
&& google_key="$(mktemp)" \
&& curl --retry 3 --retry-all-errors --retry-delay 2 \
--proto '=https' --tlsv1.2 -fsSL \
https://dl.google.com/linux/linux_signing_key.pub \
-o "${google_key}" \
&& test "$(gpg --batch --show-keys --with-colons "${google_key}" \
| awk -F: '$1 == "fpr" { print $10; exit }')" \
= "${GOOGLE_LINUX_SIGNING_KEY_FINGERPRINT}" \
&& install -d -m 0755 /etc/apt/keyrings \
&& install -m 0644 "${google_key}" /etc/apt/keyrings/google-chrome.asc \
&& printf '%s\n' \
'deb [arch=amd64 signed-by=/etc/apt/keyrings/google-chrome.asc] https://dl.google.com/linux/chrome/deb/ stable main' \
> /etc/apt/sources.list.d/google-chrome.list \
&& rm -f "${google_key}" \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
"google-chrome-stable=${GOOGLE_CHROME_VERSION}" \
&& rm -rf /var/lib/apt/lists/*
RUN node_archive="node-v${NODE_VERSION}-linux-x64.tar.xz" \
&& curl --retry 3 --retry-all-errors --retry-delay 2 \
--proto '=https' --tlsv1.2 -fsSLO \
"https://nodejs.org/dist/v${NODE_VERSION}/${node_archive}" \
&& printf '%s %s\n' "${NODE_LINUX_X64_SHA256}" "${node_archive}" \
| sha256sum --check --strict \
&& install -d -m 0755 /usr/local/lib/genarrative-node \
&& tar -xJf "${node_archive}" \
--strip-components=1 \
--directory /usr/local/lib/genarrative-node \
&& rm -f "${node_archive}" \
&& ln -sfn /usr/local/lib/genarrative-node/bin/node /usr/local/bin/node \
&& ln -sfn /usr/local/lib/genarrative-node/bin/npm /usr/local/bin/npm \
&& ln -sfn /usr/local/lib/genarrative-node/bin/npx /usr/local/bin/npx \
&& ln -sfn /usr/local/lib/genarrative-node/bin/corepack /usr/local/bin/corepack \
&& npm install --global "npm@${NPM_VERSION}" --no-audit --no-fund \
&& test "$(npm --version)" = "${NPM_VERSION}"
COPY --from=rust-dependency-cache /usr/local/cargo /usr/local/cargo
COPY --from=rust-dependency-cache /usr/local/rustup /usr/local/rustup
ARG NPM_LOCK_SHA256
ARG SERVER_RUST_LOCK_SHA256
ARG DESKTOP_RUST_LOCK_SHA256
ARG AGC_RUST_LOCK_SHA256
COPY package.json package-lock.json /usr/local/share/genarrative-ci/npm/
COPY apps/admin-web/package.json /usr/local/share/genarrative-ci/npm/apps/admin-web/package.json
COPY apps/ai-game-creator-shell/package.json /usr/local/share/genarrative-ci/npm/apps/ai-game-creator-shell/package.json
COPY apps/desktop-shell/package.json /usr/local/share/genarrative-ci/npm/apps/desktop-shell/package.json
COPY apps/mobile-shell/package.json /usr/local/share/genarrative-ci/npm/apps/mobile-shell/package.json
COPY apps/preview-deployer-web/package.json /usr/local/share/genarrative-ci/npm/apps/preview-deployer-web/package.json
COPY packages/image-canvas-core/package.json /usr/local/share/genarrative-ci/npm/packages/image-canvas-core/package.json
COPY packages/image-canvas-react/package.json /usr/local/share/genarrative-ci/npm/packages/image-canvas-react/package.json
COPY packages/shared/package.json /usr/local/share/genarrative-ci/npm/packages/shared/package.json
COPY tools/spine-json-export-validator/package.json /usr/local/share/genarrative-ci/npm/tools/spine-json-export-validator/package.json
COPY server-rs/Cargo.lock /usr/local/share/genarrative-ci/locks/server-rs.Cargo.lock
COPY apps/desktop-shell/src-tauri/Cargo.lock /usr/local/share/genarrative-ci/locks/desktop-shell.Cargo.lock
COPY apps/ai-game-creator-shell/src-tauri/Cargo.lock /usr/local/share/genarrative-ci/locks/ai-game-creator-shell.Cargo.lock
COPY deploy/container/gitea-ci-checkout.sh /usr/local/bin/genarrative-gitea-checkout
RUN test -n "${NPM_LOCK_SHA256}" \
&& test -n "${SERVER_RUST_LOCK_SHA256}" \
&& test -n "${DESKTOP_RUST_LOCK_SHA256}" \
&& test -n "${AGC_RUST_LOCK_SHA256}" \
&& printf '%s %s\n' \
"${NPM_LOCK_SHA256}" \
/usr/local/share/genarrative-ci/npm/package-lock.json \
| sha256sum --check --strict \
&& printf '%s %s\n' \
"${SERVER_RUST_LOCK_SHA256}" \
/usr/local/share/genarrative-ci/locks/server-rs.Cargo.lock \
| sha256sum --check --strict \
&& printf '%s %s\n' \
"${DESKTOP_RUST_LOCK_SHA256}" \
/usr/local/share/genarrative-ci/locks/desktop-shell.Cargo.lock \
| sha256sum --check --strict \
&& printf '%s %s\n' \
"${AGC_RUST_LOCK_SHA256}" \
/usr/local/share/genarrative-ci/locks/ai-game-creator-shell.Cargo.lock \
| sha256sum --check --strict \
&& chmod 0755 /usr/local/bin/genarrative-gitea-checkout \
&& npm ci \
--ignore-scripts \
--no-audit \
--no-fund \
--prefer-offline \
--prefix /usr/local/share/genarrative-ci/npm \
&& rm -rf \
/usr/local/share/genarrative-ci/npm/node_modules \
/usr/local/share/genarrative-ci/npm/apps/*/node_modules \
/usr/local/share/genarrative-ci/npm/packages/*/node_modules \
/usr/local/share/genarrative-ci/npm/tools/*/node_modules \
&& npm cache verify
RUN install -m 0755 /usr/local/cargo/bin/rustup /usr/local/bin/rustup \
&& for command_name in cargo cargo-fmt rustc rustdoc rustfmt; do \
ln -sfn rustup "/usr/local/bin/${command_name}"; \
done
ENV CARGO_HOME=/usr/local/cargo \
GENARRATIVE_GITEA_CI_AGC_RUST_LOCK_SHA256=${AGC_RUST_LOCK_SHA256} \
GENARRATIVE_GITEA_CI_DESKTOP_RUST_LOCK_SHA256=${DESKTOP_RUST_LOCK_SHA256} \
GENARRATIVE_GITEA_CI_NPM_LOCK_SHA256=${NPM_LOCK_SHA256} \
GENARRATIVE_GITEA_CI_NPM_VERSION=${NPM_VERSION} \
GENARRATIVE_GITEA_CI_SERVER_RUST_LOCK_SHA256=${SERVER_RUST_LOCK_SHA256} \
NPM_CONFIG_AUDIT=false \
NPM_CONFIG_FETCH_RETRIES=10 \
NPM_CONFIG_FETCH_RETRY_FACTOR=2 \
NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT=60000 \
NPM_CONFIG_FETCH_RETRY_MINTIMEOUT=2000 \
NPM_CONFIG_FUND=false \
NPM_CONFIG_PREFER_OFFLINE=true \
RUSTUP_AUTO_INSTALL=0 \
RUSTUP_HOME=/usr/local/rustup \
PATH=/usr/local/lib/genarrative-node/bin:/usr/local/bin:/usr/local/cargo/bin:/opt/acttoolcache/node/24.18.0/x64/bin:/usr/local/sbin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
ARG IMAGE_REVISION=uncommitted
LABEL org.opencontainers.image.vendor="GenarrativeAI" \
org.opencontainers.image.version="2026.08.07.1" \
org.opencontainers.image.source="https://git.genarrative.world/GenarrativeAI/Genarrative" \
org.opencontainers.image.revision="${IMAGE_REVISION}" \
org.opencontainers.image.base.name="docker.gitea.com/runner-images:ubuntu-latest@sha256:58ea92624c7c09582e05594d95488331045053d3a3f34cf09649f2a32313a614" \
com.genarrative.ci.dockerfile="deploy/container/gitea-ci-job.Dockerfile" \
com.genarrative.ci.definition-sha256="${IMAGE_REVISION}"
RUN node --version \
&& test "$(npm --version)" = "${NPM_VERSION}" \
&& rustup show active-toolchain \
&& rustc --version \
&& cargo --version \
&& rustfmt --version \
&& google-chrome --version \
&& bwrap --version \
&& test -x /usr/local/bin/genarrative-gitea-checkout \
&& bash -n /usr/local/bin/genarrative-gitea-checkout \
&& test -d /root/.npm/_cacache \
&& test -d /usr/local/cargo/registry/cache \
&& rg --version \
&& ffmpeg -version | head -n 1