ci: eliminate cold pnpm install via pre-baked CI base image (Phase 1)
Every pipeline ran a cold pnpm install (network fetch + musl native rebuilds + apk add python3 make g++), median ~731s, paid twice per push. Phase 1 (no cluster access, repo commits only): - Dockerfile.ci: node:22-alpine + python3/make/g++/postgresql-client + pnpm@10.6.2 + pnpm fetch to warm the store and compile natives once. - .woodpecker/ci-image.yml: kaniko build/push of ci-base:latest + a lockfile-hash tag, triggered only when pnpm-lock.yaml or Dockerfile.ci change. Reuses the publish.yml kaniko/auth pattern. - ci.yml + publish.yml: install from the baked ci-base:latest, drop the per-run apk add, use pnpm install --frozen-lockfile --prefer-offline. - Framework monorepo template: single cached install other steps depend on instead of re-running npm ci across 6 steps. Node 22->24 bump is a separate follow-up PR. Phase 2 (RWX Longhorn PVC) is out of scope. Expected install ~731s -> ~30-60s. Refs #634 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
40
.woodpecker/ci-image.yml
Normal file
40
.woodpecker/ci-image.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
# Build & push the pre-baked CI base image (Dockerfile.ci) to the Gitea
|
||||
# registry CI already publishes to. Reuses the exact kaniko + auth pattern
|
||||
# from publish.yml (REGISTRY_USER/REGISTRY_PASS from_secret, /kaniko/.docker
|
||||
# config.json). Other pipelines (ci.yml, publish.yml) pull `ci-base:latest`
|
||||
# for their install step.
|
||||
#
|
||||
# Rebuild ONLY when the dependency set or the image recipe changes — a normal
|
||||
# code push must not trigger a 25-min image build. `path` applies to push/PR
|
||||
# events; `event: tag` (releases) rebuilds unconditionally so a tagged release
|
||||
# always ships a fresh base.
|
||||
when:
|
||||
- event: tag
|
||||
- event: [push, manual]
|
||||
branch: main
|
||||
path:
|
||||
include:
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'Dockerfile.ci'
|
||||
|
||||
steps:
|
||||
build-ci-base:
|
||||
image: gcr.io/kaniko-project/executor:debug
|
||||
environment:
|
||||
REGISTRY_USER:
|
||||
from_secret: gitea_username
|
||||
REGISTRY_PASS:
|
||||
from_secret: gitea_password
|
||||
CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH}
|
||||
CI_COMMIT_TAG: ${CI_COMMIT_TAG}
|
||||
CI_COMMIT_SHA: ${CI_COMMIT_SHA}
|
||||
commands:
|
||||
- mkdir -p /kaniko/.docker
|
||||
- echo "{\"auths\":{\"git.mosaicstack.dev\":{\"username\":\"$REGISTRY_USER\",\"password\":\"$REGISTRY_PASS\"}}}" > /kaniko/.docker/config.json
|
||||
- |
|
||||
# Lockfile-hash tag: an immutable identity for the exact dep set baked
|
||||
# into this image. `:latest` is the mutable pointer pipelines consume.
|
||||
LOCK_HASH=$(sha256sum pnpm-lock.yaml | cut -c1-12)
|
||||
DESTINATIONS="--destination git.mosaicstack.dev/mosaicstack/stack/ci-base:latest"
|
||||
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/ci-base:lock-$LOCK_HASH"
|
||||
/kaniko/executor --context . --dockerfile Dockerfile.ci $DESTINATIONS
|
||||
@@ -1,5 +1,9 @@
|
||||
# &node_image is the pre-baked CI base built by .woodpecker/ci-image.yml:
|
||||
# node:22-alpine + python3/make/g++/postgresql-client + pnpm + a warm pnpm
|
||||
# store. The install step resolves from the baked store (--prefer-offline)
|
||||
# instead of paying a ~731s cold fetch + native compile every run.
|
||||
variables:
|
||||
- &node_image 'node:22-alpine'
|
||||
- &node_image 'git.mosaicstack.dev/mosaicstack/stack/ci-base:latest'
|
||||
- &enable_pnpm 'corepack enable'
|
||||
|
||||
when:
|
||||
@@ -15,8 +19,9 @@ steps:
|
||||
image: *node_image
|
||||
commands:
|
||||
- corepack enable
|
||||
- apk add --no-cache python3 make g++
|
||||
- pnpm install --frozen-lockfile
|
||||
# python3/make/g++ are baked into ci-base; --prefer-offline resolves from
|
||||
# the baked pnpm store.
|
||||
- pnpm install --frozen-lockfile --prefer-offline
|
||||
|
||||
# Blocking gate: public framework package must contain no operator-specific
|
||||
# personal data or private $HOME defaults. Runs early (no node_modules needed).
|
||||
@@ -64,8 +69,7 @@ steps:
|
||||
DATABASE_URL: postgresql://mosaic:mosaic@ci-postgres:5432/mosaic
|
||||
commands:
|
||||
- *enable_pnpm
|
||||
# Install postgresql-client for pg_isready
|
||||
- apk add --no-cache postgresql-client
|
||||
# postgresql-client (pg_isready) is baked into ci-base.
|
||||
# Wait up to 60s for CI postgres to be ready; fail fast if it never comes up.
|
||||
- |
|
||||
ready=0
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
# Runs only on main branch push/tag
|
||||
|
||||
variables:
|
||||
- &node_image 'node:22-alpine'
|
||||
# Pre-baked CI base (see .woodpecker/ci-image.yml): node:22-alpine +
|
||||
# toolchain + warm pnpm store. Kills the second cold install publish pays.
|
||||
- &node_image 'git.mosaicstack.dev/mosaicstack/stack/ci-base:latest'
|
||||
- &enable_pnpm 'corepack enable'
|
||||
# Heavy kaniko image builds (~25 min) — gate them so a merge that only touches
|
||||
# the npm-only CLI (@mosaicstack/mosaic) or docs does NOT rebuild the platform
|
||||
@@ -31,7 +33,8 @@ steps:
|
||||
image: *node_image
|
||||
commands:
|
||||
- corepack enable
|
||||
- pnpm install --frozen-lockfile
|
||||
# Resolve from the baked pnpm store instead of a cold network fetch.
|
||||
- pnpm install --frozen-lockfile --prefer-offline
|
||||
|
||||
build:
|
||||
image: *node_image
|
||||
|
||||
Reference in New Issue
Block a user