mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-11-15 11:20:36 +08:00
Add buildkite ci pipelines
The pipelines are supposed to be a drop-in replacement for the current github actions pipelines. The github workflows have been restructured into 4 pipelines to take advantage of the buildkite caching mechanism.
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
# Buildkite CI
|
||||
|
||||
The Garden Buildkite integration stores a bare `buildkite-agent pipeline
|
||||
upload`, so the agent finds `pipeline.yml` by its default name. The integration
|
||||
makes one pipeline per repo, so all suites land in the same build.
|
||||
|
||||
## Cache volumes and artifacts
|
||||
|
||||
A **cache volume** carries state between builds. It is an input: content that a
|
||||
step needs, but that the repo does not contain. Volume names are scoped to the
|
||||
pipeline.
|
||||
|
||||
An **artifact** is a file that one build produces. It is an output: evidence for
|
||||
a person to read in the Buildkite UI. The agent uploads artifacts whatever the
|
||||
exit code of the step, so a failed step still reports its results.
|
||||
|
||||
The visual suite uses both. The volume `visual-snapshots` holds the baseline
|
||||
images, which are gitignored and exist nowhere else. `artifact_paths` uploads
|
||||
the actual, expected, and diff images that a failed comparison writes. The two
|
||||
cannot be swapped: a volume is not visible in the UI, and an artifact does not
|
||||
persist as an input to the next build.
|
||||
|
||||
## One writer per volume
|
||||
|
||||
A job does not read its volume in place. It gets a private copy at start. That
|
||||
copy becomes the version that the next job sees, but only when the job exits 0.
|
||||
Every successful job is therefore a writer, whether or not it changed a file.
|
||||
|
||||
Two consequences follow.
|
||||
|
||||
Keep the volume names distinct across the uploaded files. A step that commits a
|
||||
version discards what a concurrent step wrote to the same volume. This is why
|
||||
`rust.yml` gives test, doc, and lint a separate volume each.
|
||||
|
||||
The visual suite keeps exactly one writer. `master` updates the baselines, and
|
||||
`concurrency: 1` serializes the group. Other branches always exit non-zero, so
|
||||
Buildkite discards their copy and no branch can republish a stale baseline set.
|
||||
The branch step then cannot use the exit code to report the test result, so two
|
||||
codes carry both: 100 means no differences, which `soft_fail` keeps green, and
|
||||
any other non-zero code fails the build. An annotation reports the outcome in
|
||||
both cases.
|
||||
|
||||
## Docker images
|
||||
|
||||
Two images cover all Node steps:
|
||||
|
||||
- `node:24-bookworm` tracks the major version in `.nvmrc` and the `engines`
|
||||
field in `package.json`.
|
||||
- `mcr.microsoft.com/playwright:v1.61.1-noble` ships the browsers at
|
||||
`/ms-playwright`, so no step installs them. The tag must match
|
||||
`@playwright/test` in `package.json`. Playwright refuses browsers built for
|
||||
another version.
|
||||
|
||||
Node steps use no cache volume. Cache paths are relative to the step working
|
||||
directory, and the agent home is not visible inside a container, so npm
|
||||
downloads its dependencies on every run. The Rust steps run on the agent
|
||||
instead, so their `~`-rooted paths work.
|
||||
@@ -0,0 +1,31 @@
|
||||
# No explicit build step: tests/support/globalSetup.ts runs `npm run build` in
|
||||
# parallel with fixture creation, and asserts the pinned rad and radicle-httpd
|
||||
# versions are on PATH.
|
||||
|
||||
env:
|
||||
CI: "true"
|
||||
|
||||
steps:
|
||||
- label: "🎭 e2e"
|
||||
key: e2e
|
||||
timeout_in_minutes: 30
|
||||
artifact_paths:
|
||||
- "tests/artifacts/**/*"
|
||||
plugins:
|
||||
# Ships the browsers at /ms-playwright, so nothing here installs them. The
|
||||
# tag must match @playwright/test in package.json — Playwright refuses
|
||||
# browsers built for another version.
|
||||
- docker#v5.14.0:
|
||||
image: "mcr.microsoft.com/playwright:v1.61.1-noble"
|
||||
# Chromium exhausts Docker's default 64MB /dev/shm.
|
||||
ipc: host
|
||||
environment:
|
||||
- CI
|
||||
commands:
|
||||
- npm ci
|
||||
# The image ships no decompressors: install-binaries unpacks .tar.xz
|
||||
# releases, and the repo fixtures are .tar.bz2 archives.
|
||||
- apt-get update -qq && apt-get install -y -qq xz-utils bzip2
|
||||
- mkdir -p tests/artifacts
|
||||
- ./scripts/install-binaries
|
||||
- npm run test:e2e -- --project chromium
|
||||
@@ -0,0 +1,73 @@
|
||||
# No cache volume: cache paths are relative to the step working directory, and
|
||||
# the agent's home is not visible inside a container, so npm downloads its
|
||||
# dependencies on every run.
|
||||
|
||||
env:
|
||||
CI: "true"
|
||||
|
||||
__defs:
|
||||
# Tracks the major version in .nvmrc and the engines field in package.json.
|
||||
node_image: &node_image
|
||||
docker#v5.14.0:
|
||||
image: "node:24-bookworm"
|
||||
environment:
|
||||
- CI
|
||||
|
||||
# Ships the browsers at /ms-playwright, which is why no step installs them.
|
||||
# The tag must match @playwright/test in package.json — Playwright refuses to
|
||||
# run against browsers built for another version.
|
||||
playwright_image: &playwright_image
|
||||
docker#v5.14.0:
|
||||
image: "mcr.microsoft.com/playwright:v1.61.1-noble"
|
||||
# Chromium exhausts Docker's default 64MB /dev/shm.
|
||||
ipc: host
|
||||
environment:
|
||||
- CI
|
||||
|
||||
steps:
|
||||
- label: "🔎 check"
|
||||
key: check
|
||||
timeout_in_minutes: 10
|
||||
plugins:
|
||||
- *node_image
|
||||
commands:
|
||||
- npm ci
|
||||
- npm run check
|
||||
|
||||
- label: "🧪 unit-tests"
|
||||
key: unit-tests
|
||||
timeout_in_minutes: 10
|
||||
plugins:
|
||||
- *node_image
|
||||
commands:
|
||||
- npm ci
|
||||
- npm run test:unit
|
||||
|
||||
# The http-client suite talks to a real radicle-httpd, so it needs the pinned
|
||||
# binaries on PATH even though it runs under vitest rather than Playwright.
|
||||
- label: "🔌 http-client-tests"
|
||||
key: http-client-tests
|
||||
timeout_in_minutes: 15
|
||||
artifact_paths:
|
||||
- "tests/artifacts/**/*"
|
||||
plugins:
|
||||
- *node_image
|
||||
commands:
|
||||
- npm ci
|
||||
- mkdir -p tests/artifacts
|
||||
- ./scripts/install-binaries
|
||||
- npm run test:http-client:unit
|
||||
|
||||
# playwright.buildSmoke.config.ts sets globalSetup to undefined, so this step
|
||||
# needs a browser but no Radicle binaries or fixtures.
|
||||
- label: "🏗 build-smoke"
|
||||
key: build-smoke
|
||||
timeout_in_minutes: 30
|
||||
artifact_paths:
|
||||
- "tests/artifacts/**/*"
|
||||
plugins:
|
||||
- *playwright_image
|
||||
commands:
|
||||
- npm ci
|
||||
- npm run build
|
||||
- npm run test:e2e -- --project chromium --config playwright.buildSmoke.config.ts
|
||||
@@ -0,0 +1,16 @@
|
||||
# Entry point for CI. garden-broker stores a bare `buildkite-agent pipeline
|
||||
# upload` as the server-side configuration, so the agent looks for this file by
|
||||
# its default name. The other files here are uploaded from below.
|
||||
#
|
||||
# The broker creates exactly one Buildkite pipeline per Radicle repo, so every
|
||||
# suite lands in this single build. Keep cache volume names distinct across the
|
||||
# uploaded files: volumes are scoped to the pipeline, and a step that commits a
|
||||
# volume version discards what a concurrent step wrote to the same volume.
|
||||
|
||||
steps:
|
||||
- label: ":pipeline: upload"
|
||||
commands:
|
||||
- buildkite-agent pipeline upload .buildkite/node-checks.yml
|
||||
- buildkite-agent pipeline upload .buildkite/e2e.yml
|
||||
- buildkite-agent pipeline upload .buildkite/visual.yml
|
||||
- buildkite-agent pipeline upload .buildkite/rust.yml
|
||||
@@ -0,0 +1,66 @@
|
||||
# CI for the Rust workspace (crates/radicle-httpd, crates/radicle-search).
|
||||
#
|
||||
# Each step gets its own named cache volume. Clippy, test, and doc each produce a
|
||||
# different target directory, and a shared volume would let whichever step
|
||||
# commits last discard the other two, forcing a near-full rebuild every build.
|
||||
#
|
||||
# These steps run on the agent rather than in a container, so ~-rooted cache
|
||||
# paths work. The toolchain version is not pinned here: rustup reads
|
||||
# ./rust-toolchain.
|
||||
#
|
||||
# Every step needs network access to seed.radicle.dev as well as crates.io — the
|
||||
# workspace patches radicle-job to a git dependency served from a Radicle seed.
|
||||
|
||||
env:
|
||||
CI: "true"
|
||||
CARGO_TERM_COLOR: "always"
|
||||
|
||||
__defs:
|
||||
install_rust: &install_rust |
|
||||
export PATH="$$HOME/.cargo/bin:$$PATH"
|
||||
export CARGO_TARGET_DIR="$$HOME/.cache/cargo-target"
|
||||
if ! command -v rustup >/dev/null 2>&1; then
|
||||
curl -fsSL https://sh.rustup.rs | sh -s -- -y --no-modify-path --profile minimal
|
||||
fi
|
||||
rustup component add rustfmt clippy
|
||||
rustc --version
|
||||
|
||||
cargo_cache: &cargo_cache
|
||||
- "~/.cargo"
|
||||
- "~/.rustup"
|
||||
- "~/.cache/cargo-target"
|
||||
|
||||
steps:
|
||||
# RUSTFLAGS is set on the build only, not on the test run.
|
||||
- label: "🦀 test"
|
||||
key: test
|
||||
timeout_in_minutes: 30
|
||||
cache:
|
||||
name: rust-test
|
||||
paths: *cargo_cache
|
||||
commands:
|
||||
- *install_rust
|
||||
- RUSTFLAGS="-D warnings" cargo build --workspace --all-features
|
||||
- cargo test --workspace --all-features
|
||||
- cargo test -p radicle-httpd
|
||||
|
||||
- label: "📚 doc"
|
||||
key: doc
|
||||
timeout_in_minutes: 30
|
||||
cache:
|
||||
name: rust-doc
|
||||
paths: *cargo_cache
|
||||
commands:
|
||||
- *install_rust
|
||||
- RUSTDOCFLAGS="-D warnings" cargo doc --workspace --all-features
|
||||
|
||||
- label: "🦀 lint"
|
||||
key: lint
|
||||
timeout_in_minutes: 30
|
||||
cache:
|
||||
name: rust-lint
|
||||
paths: *cargo_cache
|
||||
commands:
|
||||
- *install_rust
|
||||
- RUSTFLAGS="-D warnings" cargo clippy --workspace --all-features --tests
|
||||
- cargo fmt --all -- --check
|
||||
@@ -0,0 +1,107 @@
|
||||
# Visual snapshot tests.
|
||||
#
|
||||
# tests/visual/snapshots is gitignored, so the baselines live only in this cache
|
||||
# volume. Master updates them with --update-snapshots; every other branch
|
||||
# compares against them.
|
||||
#
|
||||
# A job does not read the volume in place: it gets a private copy at start, and
|
||||
# that copy becomes the version the next job sees if the job exits 0. So every
|
||||
# successful job is a writer, whether or not it changed a file. The two steps
|
||||
# below therefore keep exactly one writer:
|
||||
#
|
||||
# - Master updates the snapshots and commits, serialized by `concurrency: 1`
|
||||
# so two master builds cannot overwrite each other.
|
||||
# - Branches compare, and always exit non-zero so their copy is thrown away.
|
||||
# They need this even though they do not update the snapshots: without
|
||||
# --update-snapshots the mode is `missing`, under which Playwright still
|
||||
# writes a baseline that does not exist yet. A branch therefore needs no
|
||||
# concurrency limit, and any number of branch builds can run at once.
|
||||
|
||||
env:
|
||||
CI: "true"
|
||||
|
||||
steps:
|
||||
- label: "📸 visual update snapshots"
|
||||
key: visual-update-snapshots
|
||||
branches: "master"
|
||||
timeout_in_minutes: 30
|
||||
# One writer at a time. Buildkite runs a concurrency group in the order the
|
||||
# jobs were created, so the newest master build commits last.
|
||||
concurrency: 1
|
||||
concurrency_group: "radicle-explorer/visual-snapshots"
|
||||
cache:
|
||||
name: visual-snapshots
|
||||
paths:
|
||||
- "tests/visual/snapshots"
|
||||
artifact_paths:
|
||||
- "tests/artifacts/**/*.png"
|
||||
plugins:
|
||||
# The image ships WebKit as well as Chromium, which this suite needs: the
|
||||
# visual-mobile project uses the iPhone 13 Mini device descriptor, whose
|
||||
# defaultBrowserType is webkit. The tag must match @playwright/test in
|
||||
# package.json — Playwright refuses browsers built for another version.
|
||||
- &playwright_docker
|
||||
docker#v5.14.0:
|
||||
image: "mcr.microsoft.com/playwright:v1.61.1-noble"
|
||||
# Chromium exhausts Docker's default 64MB /dev/shm.
|
||||
ipc: host
|
||||
# buildkite-agent annotate runs inside the container.
|
||||
mount-buildkite-agent: true
|
||||
environment:
|
||||
- CI
|
||||
commands:
|
||||
- npm ci
|
||||
# The image ships no decompressors: install-binaries unpacks .tar.xz
|
||||
# releases, and the repo fixtures are .tar.bz2 archives.
|
||||
- apt-get update -qq && apt-get install -y -qq xz-utils bzip2
|
||||
- mkdir -p tests/artifacts
|
||||
- ./scripts/install-binaries
|
||||
# No `|| true` here: --update-snapshots reports a re-generated snapshot as
|
||||
# a pass, so a non-zero exit means the run itself broke. Failing is then
|
||||
# what we want, because only a job that exits 0 commits a volume version,
|
||||
# and a run that died halfway would publish a half-written baseline set.
|
||||
# The visual projects set retries: 0, so one flaky test skips the update
|
||||
# until the next push to master.
|
||||
- npm run test:e2e -- --project visual-desktop visual-mobile --update-snapshots
|
||||
|
||||
- label: "📸 visual"
|
||||
key: visual
|
||||
branches: "!master"
|
||||
timeout_in_minutes: 30
|
||||
# No concurrency limit: this job never commits a volume version.
|
||||
#
|
||||
# The job must always exit non-zero to discard its copy of the volume, so
|
||||
# the exit code cannot also report whether the tests passed. Two codes carry
|
||||
# both: 100 means no differences, and soft_fail keeps the build green.
|
||||
# Anything else fails the build — a real difference, but also a broken
|
||||
# npm ci or a crashed browser. 100 is a sentinel that no tool in this step
|
||||
# produces, so only the success path below is soft.
|
||||
soft_fail:
|
||||
- exit_status: 100
|
||||
cache:
|
||||
name: visual-snapshots
|
||||
paths:
|
||||
- "tests/visual/snapshots"
|
||||
artifact_paths:
|
||||
- "tests/artifacts/**/*.png"
|
||||
plugins:
|
||||
- *playwright_docker
|
||||
commands:
|
||||
- npm ci
|
||||
- apt-get update -qq && apt-get install -y -qq xz-utils bzip2
|
||||
- mkdir -p tests/artifacts
|
||||
- ./scripts/install-binaries
|
||||
# Both paths exit non-zero, so Buildkite discards this job's copy of the
|
||||
# volume. A zero exit would revert an update that master committed while
|
||||
# this job ran, and would also publish any baseline this run wrote itself
|
||||
# for a screenshot that has none yet.
|
||||
- |
|
||||
if npm run test:e2e -- --project visual-desktop visual-mobile; then
|
||||
buildkite-agent annotate "📸 visual: no differences" \
|
||||
--style success --context visual
|
||||
exit 100
|
||||
else
|
||||
buildkite-agent annotate "📸 visual: differences found, see the uploaded images" \
|
||||
--style warning --context visual
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user