Files
stack/.woodpecker/publish.yml
Jarvis 9da71bd861
Some checks failed
ci/woodpecker/push/ci Pipeline failed
ci/woodpecker/pr/ci Pipeline was successful
ci: switch pipelines to pre-baked ci-base image (consumer) [Phase 1b]
Consumer half of the Woodpecker CI cache work (#634). Re-scoped from the
original combined change: the image recipe (Dockerfile.ci, ci-image.yml)
now lives in the producer PR #637. This branch only flips the consumers.

- ci.yml / publish.yml: pull git.mosaicstack.dev/mosaicstack/stack/ci-base
  :latest for the install step and resolve from the baked pnpm store via
  --prefer-offline (drops the per-run apk add + cold network fetch).
- framework monorepo template: single cached install instead of npm ci per
  step, so scaffolded repos inherit the fix.

B2 fix (blocker): pin store-dir in root .npmrc to
/root/.local/share/pnpm/store — the exact path Dockerfile.ci warms — so the
pipeline install actually consumes the baked store instead of repopulating
a fresh one. The existing @mosaicstack registry line is preserved.

BLOCKED ON: PR #637 merge + a manual ci-image prime of ci-base:latest on
main. Until the image is primed this branch's CI is red (it pulls an image
that does not exist yet). Do not merge until a green re-run after priming.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 16:50:39 -05:00

198 lines
7.7 KiB
YAML

# Build, publish npm packages, and push Docker images
# Runs only on main branch push/tag
variables:
# 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
# images (gateway/appservice/web do not depend on @mosaicstack/mosaic). Releases
# (tags) always build everything. Exclude-list keeps the default SAFE: any
# non-excluded change still builds, so no transitive dep can silently go stale.
# (Woodpecker: `when` entries are OR'd; `path` applies to push/PR only — hence
# the separate `event: tag` entry.)
- &image_build_when
- event: tag
- event: [push, manual]
branch: main
path:
exclude:
- 'packages/mosaic/**'
- 'docs/**'
- '**/*.md'
- '.woodpecker/**'
when:
- branch: [main]
event: [push, manual, tag]
steps:
install:
image: *node_image
commands:
- corepack enable
# Resolve from the baked pnpm store instead of a cold network fetch.
- pnpm install --frozen-lockfile --prefer-offline
build:
image: *node_image
commands:
- *enable_pnpm
- pnpm build
depends_on:
- install
publish-npm:
image: *node_image
# Publish only when a publishable package changed (or on a release tag); a
# pure-docs merge runs no publish. Cheap step, but gated for cleanliness.
when:
- event: tag
- event: [push, manual]
branch: main
path:
include:
- 'packages/**'
environment:
NPM_TOKEN:
from_secret: gitea_token
commands:
- *enable_pnpm
# Configure auth for Gitea npm registry
- |
echo "//git.mosaicstack.dev/api/packages/mosaicstack/npm/:_authToken=$NPM_TOKEN" > ~/.npmrc
echo "@mosaicstack:registry=https://git.mosaicstack.dev/api/packages/mosaicstack/npm/" >> ~/.npmrc
# Publish non-private packages to Gitea.
#
# The only publish failure we tolerate is "version already exists" —
# that legitimately happens when only some packages were bumped in
# the merge. Any other failure (registry 404, auth error, network
# error) MUST fail the pipeline loudly: the previous
# `|| echo "... continuing"` fallback silently hid a 404 from the
# Gitea org rename and caused every @mosaicstack/* publish to fall
# on the floor while CI still reported green.
- |
# Portable sh (Alpine ash) — avoid bashisms like PIPESTATUS.
set +e
pnpm --filter "@mosaicstack/*" --filter "!@mosaicstack/web" publish --no-git-checks --access public >/tmp/publish.log 2>&1
EXIT=$?
set -e
cat /tmp/publish.log
if [ "$EXIT" -eq 0 ]; then
echo "[publish] all packages published successfully"
exit 0
fi
# Hard registry / auth / network errors → fatal. Match npm's own
# error lines specifically to avoid false positives on arbitrary
# log text that happens to contain "E404" etc.
if grep -qE "npm (error|ERR!) code (E404|E401|ENEEDAUTH|ECONNREFUSED|ETIMEDOUT|ENOTFOUND)" /tmp/publish.log; then
echo "[publish] FATAL: registry/auth/network error detected — failing pipeline" >&2
exit 1
fi
# Only tolerate the explicit "version already published" case.
# npm returns this as E403 with body "You cannot publish over..."
# or EPUBLISHCONFLICT depending on version.
if grep -qE "EPUBLISHCONFLICT|You cannot publish over|previously published" /tmp/publish.log; then
echo "[publish] some packages already at this version — continuing (non-fatal)"
exit 0
fi
echo "[publish] FATAL: publish failed with unrecognized error — failing pipeline" >&2
exit 1
depends_on:
- build
# TODO: Uncomment when ready to publish to npmjs.org
# publish-npmjs:
# image: *node_image
# environment:
# NPM_TOKEN:
# from_secret: npmjs_token
# commands:
# - *enable_pnpm
# - apk add --no-cache jq bash
# - bash scripts/publish-npmjs.sh
# depends_on:
# - build
# when:
# - event: [tag]
build-gateway:
image: gcr.io/kaniko-project/executor:debug
when: *image_build_when
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
- |
DESTINATIONS="--destination git.mosaicstack.dev/mosaicstack/stack/gateway:sha-${CI_COMMIT_SHA:0:7}"
if [ "$CI_COMMIT_BRANCH" = "main" ]; then
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/gateway:latest"
fi
if [ -n "$CI_COMMIT_TAG" ]; then
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/gateway:$CI_COMMIT_TAG"
fi
/kaniko/executor --context . --dockerfile docker/gateway.Dockerfile $DESTINATIONS
depends_on:
- build
build-appservice:
image: gcr.io/kaniko-project/executor:debug
when: *image_build_when
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
- |
DESTINATIONS="--destination git.mosaicstack.dev/mosaicstack/stack/appservice:sha-${CI_COMMIT_SHA:0:7}"
if [ "$CI_COMMIT_BRANCH" = "main" ]; then
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/appservice:latest"
fi
if [ -n "$CI_COMMIT_TAG" ]; then
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/appservice:$CI_COMMIT_TAG"
fi
/kaniko/executor --context . --dockerfile docker/appservice.Dockerfile $DESTINATIONS
depends_on:
- build
build-web:
image: gcr.io/kaniko-project/executor:debug
when: *image_build_when
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
- |
DESTINATIONS="--destination git.mosaicstack.dev/mosaicstack/stack/web:sha-${CI_COMMIT_SHA:0:7}"
if [ "$CI_COMMIT_BRANCH" = "main" ]; then
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/web:latest"
fi
if [ -n "$CI_COMMIT_TAG" ]; then
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/web:$CI_COMMIT_TAG"
fi
/kaniko/executor --context . --dockerfile docker/web.Dockerfile $DESTINATIONS
depends_on:
- build