ci: Ubicloud is the main CI — retire the Hetzner VM workflows
ci-ubicloud.yml (cherry-picked from the ubicloud-ci experiment branch, green on run 27375556034: 291 wx + 38 kicad tests in 1h52m) now triggers on main pushes and replaces ci-full-build.yml and ci.yml. The Hetzner flow needed create/delete-VM jobs, a PAT + hcloud token, and a shared one-slot concurrency lock; the Ubicloud runner is a plain runs-on label and finished faster end-to-end. wasm-opt-bench.yml stays (dispatch-only Hetzner bench harness, unrelated to CI). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
c7e507ced6
commit
e932530afb
3 changed files with 8 additions and 341 deletions
142
.github/workflows/ci-full-build.yml
vendored
142
.github/workflows/ci-full-build.yml
vendored
|
|
@ -1,142 +0,0 @@
|
||||||
# Full KiCad WASM build (all 6 tools) + e2e on an ephemeral Hetzner ccx53.
|
|
||||||
# This is the main-branch CI — the ONLY push-triggered Hetzner workflow; the
|
|
||||||
# bench (wasm-opt-bench.yml) and feature-branch (ci.yml) workflows are
|
|
||||||
# workflow_dispatch-only to keep paid VM spend deliberate. Validated at ~1h15m
|
|
||||||
# (run 27280051992, was 4h05m): lifted docker CPU caps, pipelined host-side
|
|
||||||
# wasm-opt (KICAD_PIPELINE=1), self-built Binaryen v130. History and
|
|
||||||
# measurements in docs/ci-build-slowness-findings.md.
|
|
||||||
|
|
||||||
name: CI full build + e2e (Hetzner)
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: ["main"]
|
|
||||||
# docs-only commits must not burn a ~1h15m Hetzner slot
|
|
||||||
paths-ignore: ["docs/**", "**.md"]
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
binaryen_version: { description: "Binaryen version for the host asyncify+-O2 step", required: false, default: "130" }
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: wasm-opt-bench # share the one-Hetzner-slot lock with the sweeps
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
create-runner:
|
|
||||||
name: Create Hetzner runner
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
label: ${{ steps.create.outputs.label }}
|
|
||||||
server_id: ${{ steps.create.outputs.server_id }}
|
|
||||||
steps:
|
|
||||||
- uses: Cyclenerd/hcloud-github-runner@v1
|
|
||||||
id: create
|
|
||||||
with:
|
|
||||||
mode: create
|
|
||||||
github_token: ${{ secrets.HETZNER_RUNNER_PAT }}
|
|
||||||
hcloud_token: ${{ secrets.HCLOUD_TOKEN }}
|
|
||||||
server_type: ccx53
|
|
||||||
location: nbg1
|
|
||||||
image: ubuntu-24.04
|
|
||||||
|
|
||||||
validate:
|
|
||||||
name: Build all tools + KiCad e2e
|
|
||||||
needs: create-runner
|
|
||||||
runs-on: ${{ needs.create-runner.outputs.label }}
|
|
||||||
timeout-minutes: 300
|
|
||||||
env:
|
|
||||||
KICAD_LOG_NESTED: "1"
|
|
||||||
BINARYEN_VERSION: ${{ inputs.binaryen_version || '130' }}
|
|
||||||
steps:
|
|
||||||
- name: Install system deps (Docker, git)
|
|
||||||
run: |
|
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
|
||||||
sudo apt-get update
|
|
||||||
# xvfb: kicad e2e runs headed Firefox under a virtual display — headless
|
|
||||||
# Firefox cannot create any GL context on the GPU-less VM (see
|
|
||||||
# tests/playwright-kicad.config.ts CI prefs).
|
|
||||||
sudo apt-get install -y ca-certificates curl git libjemalloc2 xvfb
|
|
||||||
sudo install -m 0755 -d /etc/apt/keyrings
|
|
||||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
|
||||||
sudo chmod a+r /etc/apt/keyrings/docker.gpg
|
|
||||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
|
|
||||||
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" \
|
|
||||||
| sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
|
||||||
# toolchain for BINARYEN_BUILD_FROM_SOURCE (get-wasm-opt.sh)
|
|
||||||
sudo apt-get install -y cmake ninja-build g++
|
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with: { submodules: recursive }
|
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with: { node-version: 20 }
|
|
||||||
|
|
||||||
# Full KiCad WASM build of ALL 6 tools, but the host-side asyncify + -O2 use
|
|
||||||
# Binaryen v130 (BINARYEN_VERSION in env → get-wasm-opt.sh forces that
|
|
||||||
# standalone release). 'all' = pcbnew eeschema calculator pl_editor
|
|
||||||
# symbol_editor gerbview, built in sequence.
|
|
||||||
- name: Build all KiCad tools WASM with Binaryen ${{ env.BINARYEN_VERSION }}
|
|
||||||
run: |
|
|
||||||
# Lift the docker-compose dev-Mac caps (10 CPUs / 32G): run 27226030304
|
|
||||||
# compiled on 10 of 32 cores with -j 32. Pipeline the host-side
|
|
||||||
# asyncify+-O2 with the next tool's container compile (KICAD_PIPELINE=1;
|
|
||||||
# wasm-opt only reaches ~350% CPU, the container is idle meanwhile).
|
|
||||||
# BINARYEN_CORES=16: -O2 is Amdahl-capped ~4 effective cores, and up to
|
|
||||||
# two postprocesses run concurrently next to the compile.
|
|
||||||
export KICAD_DOCKER_CPUS="$(nproc)" KICAD_DOCKER_MEM=110G
|
|
||||||
export KICAD_PIPELINE=1 BINARYEN_CORES=16
|
|
||||||
# Self-built wasm-opt: the official x86_64-linux tarball runs asyncify
|
|
||||||
# 4x slower than a stock gcc -O3+LTO build, identical output
|
|
||||||
# (A/B on this runner, run 27276830256). ~5 min build, instantly repaid.
|
|
||||||
export BINARYEN_BUILD_FROM_SOURCE=1
|
|
||||||
echo "Building ALL tools with BINARYEN_VERSION=${BINARYEN_VERSION}, compile -j $(nproc) on ${KICAD_DOCKER_CPUS} CPUs, pipelined wasm-opt"
|
|
||||||
./docker/build.sh all --build-deps -j "$(nproc)"
|
|
||||||
echo "wasm-opt used:"; ./scripts/common/get-wasm-opt.sh --version 2>/dev/null || true
|
|
||||||
ls -lh output/*.wasm
|
|
||||||
|
|
||||||
- name: Install test deps
|
|
||||||
working-directory: tests
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Install Playwright browsers
|
|
||||||
working-directory: tests
|
|
||||||
run: npx playwright install --with-deps firefox chromium
|
|
||||||
|
|
||||||
- name: Stage KiCad WASM for tests
|
|
||||||
working-directory: tests
|
|
||||||
run: npm run setup:kicad
|
|
||||||
|
|
||||||
- name: KiCad e2e (npm run test:kicad:ci)
|
|
||||||
working-directory: tests
|
|
||||||
# xvfb-run: headed Firefox on a virtual display — WebGL needs GLX + Mesa
|
|
||||||
# llvmpipe, which headless Firefox can't reach on a GPU-less VM.
|
|
||||||
# test:kicad:ci adds the chromium-ci project: pcbnew's wasm exceeds
|
|
||||||
# SpiderMonkey's x86-64 code budget, so those specs run on Chromium.
|
|
||||||
run: xvfb-run -a npm run test:kicad:ci
|
|
||||||
|
|
||||||
- name: Upload test logs & screenshots
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: full-build-e2e-${{ github.run_id }}
|
|
||||||
path: |
|
|
||||||
tests/logs/**
|
|
||||||
tests/test-results/**
|
|
||||||
tests/playwright-report/**
|
|
||||||
if-no-files-found: ignore
|
|
||||||
|
|
||||||
delete-runner:
|
|
||||||
name: Delete Hetzner runner
|
|
||||||
needs: [create-runner, validate]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: always()
|
|
||||||
steps:
|
|
||||||
- uses: Cyclenerd/hcloud-github-runner@v1
|
|
||||||
with:
|
|
||||||
mode: delete
|
|
||||||
github_token: ${{ secrets.HETZNER_RUNNER_PAT }}
|
|
||||||
hcloud_token: ${{ secrets.HCLOUD_TOKEN }}
|
|
||||||
name: ${{ needs.create-runner.outputs.label }}
|
|
||||||
server_id: ${{ needs.create-runner.outputs.server_id }}
|
|
||||||
16
.github/workflows/ci-ubicloud.yml
vendored
16
.github/workflows/ci-ubicloud.yml
vendored
|
|
@ -1,16 +1,16 @@
|
||||||
# EXPERIMENT: same full build + e2e as ci-full-build.yml, but on a Ubicloud
|
# The main-branch CI: full KiCad WASM build (all 6 tools, Binaryen 130) +
|
||||||
# runner instead of an ephemeral Hetzner VM. Ubicloud runners are plain
|
# wxWidgets e2e (291 tests) + KiCad e2e (38 tests) on a Ubicloud runner.
|
||||||
# runs-on labels — no create/delete-runner jobs, no shared one-slot lock.
|
# Replaced the ephemeral-Hetzner-VM workflows (ci-full-build.yml / ci.yml,
|
||||||
# Triggered ONLY on the ubicloud-ci branch (and manual dispatch) so it never
|
# removed 2026-06-12): Ubicloud runners are plain runs-on labels — no
|
||||||
# competes with the Hetzner main CI. ubicloud-standard-30 (x86, 30 vcpu) is
|
# create/delete-runner jobs, no shared one-slot lock — and benchmarked
|
||||||
# the closest parity to the Hetzner ccx53 (32 cores) and matches the arch the
|
# faster end-to-end. Proven green: run 27375556034 (329 tests, 1h52m).
|
||||||
# e2e screenshot baselines were captured on.
|
# ubicloud-standard-30 (x86, 30 vcpu) ≈ the old Hetzner ccx53.
|
||||||
|
|
||||||
name: CI full build + e2e (Ubicloud)
|
name: CI full build + e2e (Ubicloud)
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["ubicloud-ci"]
|
branches: ["main", "ubicloud-ci"]
|
||||||
# docs-only commits must not burn a paid runner slot
|
# docs-only commits must not burn a paid runner slot
|
||||||
paths-ignore: ["docs/**", "**.md"]
|
paths-ignore: ["docs/**", "**.md"]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
|
||||||
191
.github/workflows/ci.yml
vendored
191
.github/workflows/ci.yml
vendored
|
|
@ -1,191 +0,0 @@
|
||||||
# Ephemeral Hetzner Cloud CI: a fresh ccx53 VM is created per run, the full
|
|
||||||
# build + e2e suites run on it, then it is destroyed. No persistent infra.
|
|
||||||
#
|
|
||||||
# Lifecycle (Cyclenerd/hcloud-github-runner): create-runner spins up a Hetzner
|
|
||||||
# VM registered as an ephemeral self-hosted runner -> build-and-test runs on it
|
|
||||||
# -> delete-runner tears it down (always, even on failure -> no lingering cost).
|
|
||||||
#
|
|
||||||
# Required repo secrets (emergence-engineering/pcbjam -> Settings -> Secrets):
|
|
||||||
# HCLOUD_TOKEN Hetzner Cloud API token, Read & Write
|
|
||||||
# HETZNER_RUNNER_PAT GitHub PAT with Administration: read & write (registers
|
|
||||||
# and unregisters the self-hosted runner)
|
|
||||||
#
|
|
||||||
# Submodules: kicad/wxwidgets use SSH URLs (.gitmodules) but point at PUBLIC repos,
|
|
||||||
# so actions/checkout rewrites them to HTTPS and the default GITHUB_TOKEN reads them.
|
|
||||||
|
|
||||||
name: CI (Hetzner)
|
|
||||||
|
|
||||||
# Manual-only (workflow_dispatch): every run costs a paid ephemeral Hetzner VM.
|
|
||||||
# Main-branch CI is ci-full-build.yml.
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
binaryen_cores:
|
|
||||||
description: "BINARYEN_CORES for the wasm-opt/asyncify step (blank = all cores / nproc). Use to A/B core counts."
|
|
||||||
required: false
|
|
||||||
default: ""
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ci-hetzner-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
create-runner:
|
|
||||||
name: Create Hetzner runner
|
|
||||||
runs-on: ubuntu-latest # or ubicloud-standard-2 if GitHub-hosted is disabled
|
|
||||||
outputs:
|
|
||||||
label: ${{ steps.create.outputs.label }}
|
|
||||||
server_id: ${{ steps.create.outputs.server_id }}
|
|
||||||
steps:
|
|
||||||
- name: Create ephemeral ccx53 runner
|
|
||||||
id: create
|
|
||||||
uses: Cyclenerd/hcloud-github-runner@v1
|
|
||||||
with:
|
|
||||||
mode: create
|
|
||||||
github_token: ${{ secrets.HETZNER_RUNNER_PAT }}
|
|
||||||
hcloud_token: ${{ secrets.HCLOUD_TOKEN }}
|
|
||||||
server_type: ccx53
|
|
||||||
location: nbg1
|
|
||||||
image: ubuntu-24.04
|
|
||||||
|
|
||||||
build-and-test:
|
|
||||||
name: Build & test (Hetzner ccx53)
|
|
||||||
needs: create-runner
|
|
||||||
runs-on: ${{ needs.create-runner.outputs.label }}
|
|
||||||
timeout-minutes: 160 # cold --build-deps dominates; + wx build + both suites
|
|
||||||
env:
|
|
||||||
# Stream build-script output straight to the Actions log instead of
|
|
||||||
# redirecting it to logs/<script>/<ts>.log (see scripts/common/logging.sh,
|
|
||||||
# which returns early when KICAD_LOG_NESTED=1). Nothing consumes
|
|
||||||
# KICAD_LOG_FILE, so this is safe and gives live, greppable CI output.
|
|
||||||
KICAD_LOG_NESTED: "1"
|
|
||||||
steps:
|
|
||||||
# Hetzner's ubuntu-24.04 cloud image ships neither Docker nor git (Ubicloud
|
|
||||||
# preinstalled both). Install Docker CE + git before the Docker-based build.
|
|
||||||
- name: Install system deps (Docker, git)
|
|
||||||
run: |
|
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
|
||||||
sudo apt-get update
|
|
||||||
# libjemalloc2: scalable allocator preloaded into the host-side wasm-opt
|
|
||||||
# passes (scripts/common/apply-asyncify.sh) to avoid glibc malloc
|
|
||||||
# arena-lock contention, which otherwise burns ~half of wasm-opt's CPU
|
|
||||||
# in futex lock-spin under many threads.
|
|
||||||
# autoconf/automake: build-wxuniversal-wasm.sh regenerates wxWidgets'
|
|
||||||
# ./configure from configure.in on a fresh checkout (git doesn't
|
|
||||||
# preserve mtimes, so configure.in looks newer). make: the host-side
|
|
||||||
# wxWidgets + wasm-test builds drive emconfigure/emmake/make directly.
|
|
||||||
# All of this runs on the bare host (not in Docker), and the Hetzner
|
|
||||||
# ubuntu-24.04 cloud image is minimal, so these must be installed here.
|
|
||||||
# xvfb: kicad e2e runs headed Firefox under a virtual display — headless
|
|
||||||
# Firefox cannot create any GL context on the GPU-less VM (see
|
|
||||||
# tests/playwright-kicad.config.ts CI prefs).
|
|
||||||
sudo apt-get install -y ca-certificates curl git libjemalloc2 autoconf automake make xvfb
|
|
||||||
# toolchain for BINARYEN_BUILD_FROM_SOURCE (get-wasm-opt.sh)
|
|
||||||
sudo apt-get install -y cmake ninja-build g++
|
|
||||||
sudo install -m 0755 -d /etc/apt/keyrings
|
|
||||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
|
|
||||||
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
|
||||||
sudo chmod a+r /etc/apt/keyrings/docker.gpg
|
|
||||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
|
|
||||||
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" \
|
|
||||||
| sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y docker-ce docker-ce-cli containerd.io \
|
|
||||||
docker-buildx-plugin docker-compose-plugin
|
|
||||||
|
|
||||||
- name: Checkout (with submodules)
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
|
|
||||||
- name: Show disk space
|
|
||||||
run: df -h
|
|
||||||
|
|
||||||
- name: Set up Node
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 20
|
|
||||||
|
|
||||||
# 1) Cold build KiCad WASM app + all dependencies (Docker; longest step).
|
|
||||||
# ccx53 has 32 vCPU / 128 GB RAM, so use every core: -j $(nproc) for the
|
|
||||||
# C++ compile and, by default, for the host-side wasm-opt/asyncify
|
|
||||||
# (apply-asyncify.sh runs on the host and reads BINARYEN_CORES). The
|
|
||||||
# workflow_dispatch input overrides BINARYEN_CORES so we can A/B core
|
|
||||||
# counts (e.g. 8 vs 32) without editing the workflow; blank = nproc.
|
|
||||||
- name: Build KiCad WASM (all, --build-deps)
|
|
||||||
env:
|
|
||||||
BINARYEN_CORES_INPUT: ${{ github.event.inputs.binaryen_cores }}
|
|
||||||
run: |
|
|
||||||
CORES="${BINARYEN_CORES_INPUT:-$(nproc)}"
|
|
||||||
# Lift the docker-compose dev-Mac resource caps (10 CPUs / 32G) so the
|
|
||||||
# container compile actually uses the ccx53's 32 cores / 128 GB.
|
|
||||||
export KICAD_DOCKER_CPUS="$(nproc)" KICAD_DOCKER_MEM=110G
|
|
||||||
# Self-built wasm-opt: official Linux tarball runs asyncify 4x slower
|
|
||||||
# (see get-wasm-opt.sh / run 27276830256). Identical output, sha256-verified.
|
|
||||||
export BINARYEN_BUILD_FROM_SOURCE=1
|
|
||||||
echo "Build parallelism: compile -j $(nproc) on ${KICAD_DOCKER_CPUS} CPUs, BINARYEN_CORES=${CORES}"
|
|
||||||
BINARYEN_CORES="${CORES}" ./docker/build.sh eeschema --build-deps -j "$(nproc)"
|
|
||||||
|
|
||||||
# 2) Host-side wxWidgets build that produces the wxWidgets test apps consumed
|
|
||||||
# by `npm run test` (NOT produced by docker/build.sh). Auto-installs emsdk.
|
|
||||||
- name: Build wxWidgets (wxUniversal WASM)
|
|
||||||
run: ./scripts/build-wxuniversal-wasm.sh
|
|
||||||
|
|
||||||
- name: Build wxWidgets test apps
|
|
||||||
run: ./scripts/build-wasm-test.sh
|
|
||||||
|
|
||||||
# 3) Test deps + browsers (firefox for KiCad headless, chromium for wxWidgets).
|
|
||||||
- name: Install test deps
|
|
||||||
working-directory: tests
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Install Playwright browsers
|
|
||||||
working-directory: tests
|
|
||||||
run: npx playwright install --with-deps firefox chromium
|
|
||||||
|
|
||||||
# 4) Stage KiCad artifacts from ./output into tests/apps/kicad.
|
|
||||||
- name: Setup KiCad WASM for tests
|
|
||||||
working-directory: tests
|
|
||||||
run: npm run setup:kicad
|
|
||||||
|
|
||||||
# 5) Both e2e suites. GitHub sets CI=true automatically -> workers:1, retries,
|
|
||||||
# fresh per-run web server (see playwright configs).
|
|
||||||
- name: wxWidgets e2e (npm run test)
|
|
||||||
working-directory: tests
|
|
||||||
run: npm run test
|
|
||||||
|
|
||||||
- name: KiCad e2e (npm run test:kicad:ci)
|
|
||||||
working-directory: tests
|
|
||||||
# xvfb-run: headed Firefox on a virtual display — WebGL needs GLX + Mesa
|
|
||||||
# llvmpipe, which headless Firefox can't reach on a GPU-less VM.
|
|
||||||
# test:kicad:ci adds the chromium-ci project: pcbnew's wasm exceeds
|
|
||||||
# SpiderMonkey's x86-64 code budget, so those specs run on Chromium.
|
|
||||||
run: xvfb-run -a npm run test:kicad:ci
|
|
||||||
|
|
||||||
# 6) Always capture logs/screenshots for post-mortem.
|
|
||||||
- name: Upload test logs & results
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: e2e-logs
|
|
||||||
path: |
|
|
||||||
tests/logs/**
|
|
||||||
tests/test-results/**
|
|
||||||
tests/playwright-report/**
|
|
||||||
logs/**
|
|
||||||
if-no-files-found: ignore
|
|
||||||
|
|
||||||
delete-runner:
|
|
||||||
name: Delete Hetzner runner
|
|
||||||
needs: [create-runner, build-and-test]
|
|
||||||
runs-on: ubuntu-latest # match create-runner's choice
|
|
||||||
if: always() # tear down even if the build fails -> no lingering cost
|
|
||||||
steps:
|
|
||||||
- name: Delete ephemeral runner
|
|
||||||
uses: Cyclenerd/hcloud-github-runner@v1
|
|
||||||
with:
|
|
||||||
mode: delete
|
|
||||||
github_token: ${{ secrets.HETZNER_RUNNER_PAT }}
|
|
||||||
hcloud_token: ${{ secrets.HCLOUD_TOKEN }}
|
|
||||||
name: ${{ needs.create-runner.outputs.label }}
|
|
||||||
server_id: ${{ needs.create-runner.outputs.server_id }}
|
|
||||||
Loading…
Reference in a new issue