ci: ephemeral Hetzner CI — full build + e2e on main push, all else dispatch-only
Per run, a fresh ccx53 VM is created (Cyclenerd/hcloud-github-runner), builds + tests run on it, and it is always torn down — no persistent infra, no lingering cost. Secrets: HCLOUD_TOKEN, HETZNER_RUNNER_PAT. - ci-full-build.yml: the main-branch CI and the only push-triggered workflow. Full build of all 6 tools (pipelined wasm-opt, self-built Binaryen 130, ~1h15m) + KiCad e2e; docs-only pushes excluded via paths-ignore. - ci.yml (eeschema-only smoke) and wasm-opt-bench.yml (config sweeps): workflow_dispatch-only, since every push-run costs a paid VM. - Shared one-Hetzner-slot concurrency group across the workflows. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
df25275843
commit
6953fbd894
3 changed files with 443 additions and 25 deletions
135
.github/workflows/ci-full-build.yml
vendored
Normal file
135
.github/workflows/ci-full-build.yml
vendored
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
# 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
|
||||
sudo apt-get install -y ca-certificates curl git libjemalloc2
|
||||
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)
|
||||
working-directory: tests
|
||||
run: npm run test:kicad
|
||||
|
||||
- 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 }}
|
||||
136
.github/workflows/ci.yml
vendored
136
.github/workflows/ci.yml
vendored
|
|
@ -1,43 +1,99 @@
|
|||
# Cold build of all KiCad WASM apps (with deps) + full e2e suites, on Ubicloud.
|
||||
# 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.
|
||||
#
|
||||
# Runs on Ubicloud's managed GitHub Actions runners. Premium runners (AMD Ryzen 9
|
||||
# 7950X3D, ~2x faster) are enabled at the ACCOUNT level in the Ubicloud Console
|
||||
# (GitHub Runners -> Settings -> Enable Premium Runners); jobs on the
|
||||
# `ubicloud-standard-*` labels below are then auto-routed to the premium fleet with
|
||||
# no change needed here.
|
||||
# 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).
|
||||
#
|
||||
# One-time setup required:
|
||||
# The kicad/wxwidgets submodules are private SSH repos in the VV-EE org, which the
|
||||
# default GITHUB_TOKEN cannot read. Add a repo secret `SUBMODULES_TOKEN` (a PAT with
|
||||
# read access to VV-EE/kicad-source-mirror and VV-EE/wxWidgets); actions/checkout
|
||||
# rewrites the SSH submodule URLs to token-authenticated HTTPS.
|
||||
# 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
|
||||
name: CI (Hetzner)
|
||||
|
||||
# Manual-only (workflow_dispatch): every run costs a paid ephemeral Hetzner VM.
|
||||
# Main-branch CI is ci-full-build.yml.
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
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-${{ github.ref }}
|
||||
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:
|
||||
runs-on: ubicloud-standard-16
|
||||
timeout-minutes: 240 # cold --build-deps dominates; + wx build + both suites
|
||||
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). Nothing consumes
|
||||
# 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.
|
||||
sudo apt-get install -y ca-certificates curl git libjemalloc2 autoconf automake make
|
||||
# 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
|
||||
token: ${{ secrets.SUBMODULES_TOKEN }}
|
||||
|
||||
- name: Show disk space
|
||||
run: df -h
|
||||
|
|
@ -47,13 +103,28 @@ jobs:
|
|||
with:
|
||||
node-version: 20
|
||||
|
||||
# 1) Cold build every KiCad WASM app + all dependencies (Docker; longest step).
|
||||
# Output lands in ./output/.
|
||||
# 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)
|
||||
run: ./docker/build.sh 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.
|
||||
# 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
|
||||
|
||||
|
|
@ -96,3 +167,18 @@ jobs:
|
|||
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 }}
|
||||
|
|
|
|||
197
.github/workflows/wasm-opt-bench.yml
vendored
Normal file
197
.github/workflows/wasm-opt-bench.yml
vendored
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
# Dedicated benchmark of the host-side `wasm-opt -O2` pass (the ~80-min CI
|
||||
# bottleneck). Same ephemeral Hetzner ccx53 as the main CI, but instead of the
|
||||
# full build+e2e it builds the asyncified `-O2` INPUT once, caches it as an
|
||||
# artifact, and replays scripts/bench/o2-config-sweep.sh over it under a matrix
|
||||
# of allocator/THP/thread configs. Goal: find the env that kills the kernel
|
||||
# page-management storm (see docs/ci-build-slowness-findings.md).
|
||||
#
|
||||
# Manual-only (workflow_dispatch): every run costs a paid ephemeral Hetzner VM,
|
||||
# so nothing here triggers on push. Dispatch from the Actions tab (the workflow
|
||||
# must exist on the default branch for that).
|
||||
#
|
||||
# Parameters live in scripts/bench/sweep.conf (committed) or the dispatch
|
||||
# inputs below. To reuse a prior run's fixture (skip the ~40-min build), set
|
||||
# fixture_run_id to that run's id.
|
||||
#
|
||||
# Reuses the main CI's secrets: HCLOUD_TOKEN, HETZNER_RUNNER_PAT.
|
||||
|
||||
name: wasm-opt bench
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
configs: { description: "space-separated config presets (override conf)", required: false, default: "" }
|
||||
cores: { description: "BINARYEN_CORES (override conf)", required: false, default: "" }
|
||||
fixture_run_id: { description: "run id to pull the cached fixture from (blank = build it)", required: false, default: "" }
|
||||
diagnostic: { description: "1 = capture perf/vmstat/irq around first config", required: false, default: "" }
|
||||
cap_seconds: { description: "windowed-sample seconds per config (0 = run to completion)", required: false, default: "" }
|
||||
|
||||
# One Hetzner bench VM at a time. (The main CI uses a different runner; still,
|
||||
# don't push the ci-hetzner branches while a bench runs — we only have one slot.)
|
||||
concurrency:
|
||||
group: wasm-opt-bench
|
||||
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:
|
||||
- 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
|
||||
|
||||
bench:
|
||||
name: wasm-opt -O2 config sweep (Hetzner ccx53)
|
||||
needs: create-runner
|
||||
runs-on: ${{ needs.create-runner.outputs.label }}
|
||||
timeout-minutes: 210
|
||||
env:
|
||||
KICAD_LOG_NESTED: "1" # live build logs straight to the Actions console
|
||||
steps:
|
||||
- name: Checkout (with submodules)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Resolve sweep parameters
|
||||
id: params
|
||||
# Pass workflow_dispatch inputs via env (never interpolate ${{ }} into a
|
||||
# shell body) so a value can't be parsed as shell. They override the
|
||||
# committed conf when non-empty; the run id is validated numeric before
|
||||
# it reaches download-artifact.
|
||||
env:
|
||||
IN_CONFIGS: ${{ inputs.configs }}
|
||||
IN_CORES: ${{ inputs.cores }}
|
||||
IN_FIXTURE_RUN_ID: ${{ inputs.fixture_run_id }}
|
||||
IN_DIAGNOSTIC: ${{ inputs.diagnostic }}
|
||||
IN_CAP: ${{ inputs.cap_seconds }}
|
||||
run: |
|
||||
set -e
|
||||
CONF="scripts/bench/sweep.conf"
|
||||
[ -f "$CONF" ] && . "$CONF" || true
|
||||
CONFIGS="${IN_CONFIGS:-${CONFIGS_CONF:-baseline}}"
|
||||
CORES="${IN_CORES:-${CORES_CONF:-}}"
|
||||
FIXTURE_RUN_ID="${IN_FIXTURE_RUN_ID:-${FIXTURE_RUN_ID_CONF:-}}"
|
||||
DIAGNOSTIC="${IN_DIAGNOSTIC:-${DIAGNOSTIC_CONF:-0}}"
|
||||
CAP_SECONDS="${IN_CAP:-${CAP_SECONDS_CONF:-0}}"
|
||||
if [ -n "$FIXTURE_RUN_ID" ] && ! printf '%s' "$FIXTURE_RUN_ID" | grep -qE '^[0-9]+$'; then
|
||||
echo "::error::FIXTURE_RUN_ID must be numeric (got '$FIXTURE_RUN_ID')"; exit 1
|
||||
fi
|
||||
BUILD_FIXTURE=true; [ -n "$FIXTURE_RUN_ID" ] && BUILD_FIXTURE=false
|
||||
{
|
||||
echo "CONFIGS=$CONFIGS"
|
||||
echo "CORES=$CORES"
|
||||
echo "FIXTURE_RUN_ID=$FIXTURE_RUN_ID"
|
||||
echo "DIAGNOSTIC=$DIAGNOSTIC"
|
||||
echo "CAP_SECONDS=$CAP_SECONDS"
|
||||
echo "BUILD_FIXTURE=$BUILD_FIXTURE"
|
||||
} >> "$GITHUB_ENV"
|
||||
echo "build_fixture=$BUILD_FIXTURE" >> "$GITHUB_OUTPUT"
|
||||
echo "::notice::configs='$CONFIGS' cores='${CORES:-nproc}' cap=${CAP_SECONDS}s build_fixture=$BUILD_FIXTURE fixture_run_id='${FIXTURE_RUN_ID:-none}' diagnostic=$DIAGNOSTIC"
|
||||
|
||||
- name: Show machine
|
||||
run: |
|
||||
echo "nproc=$(nproc)"; free -h; df -h /
|
||||
echo "THP: $(cat /sys/kernel/mm/transparent_hugepage/enabled)"; uname -a
|
||||
|
||||
# Allocators + measurement tools. mimalloc is the key data point (Binaryen
|
||||
# #5561): prefer the distro package, source-build it if not packaged.
|
||||
- name: Install bench deps (allocators, time, perf, strace)
|
||||
run: |
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y time strace libjemalloc2 cmake g++ git curl ca-certificates \
|
||||
linux-tools-common linux-tools-generic "linux-tools-$(uname -r)" || \
|
||||
sudo apt-get install -y time strace libjemalloc2 cmake g++ git curl ca-certificates linux-tools-common linux-tools-generic
|
||||
sudo apt-get install -y libmimalloc2.0 || sudo apt-get install -y libmimalloc-dev || true
|
||||
if ! ls /usr/lib/$(uname -m)-linux-gnu/libmimalloc.so* >/dev/null 2>&1; then
|
||||
echo "mimalloc not packaged — building from source"
|
||||
git clone --depth 1 https://github.com/microsoft/mimalloc /tmp/mimalloc
|
||||
cmake -S /tmp/mimalloc -B /tmp/mimalloc/out -DCMAKE_BUILD_TYPE=Release >/dev/null
|
||||
cmake --build /tmp/mimalloc/out -j"$(nproc)" >/dev/null
|
||||
sudo cp -av /tmp/mimalloc/out/libmimalloc.so* /usr/lib/$(uname -m)-linux-gnu/
|
||||
fi
|
||||
echo "allocators present:"; ls -l /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so* /usr/lib/$(uname -m)-linux-gnu/libmimalloc.so* 2>/dev/null || true
|
||||
|
||||
# ---- Fixture: build once (asyncify, no -O2) and cache, or download it ----
|
||||
- name: Install Docker (fixture build only)
|
||||
if: steps.params.outputs.build_fixture == 'true'
|
||||
run: |
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
sudo apt-get install -y ca-certificates curl
|
||||
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: Build asyncified fixture (ASYNCIFY_ONLY, no -O2)
|
||||
if: steps.params.outputs.build_fixture == 'true'
|
||||
run: |
|
||||
# Stop after the asyncify pass so the artifact is exactly the -O2 INPUT.
|
||||
ASYNCIFY_ONLY=1 BINARYEN_CORES="$(nproc)" ./docker/build.sh eeschema --build-deps -j "$(nproc)"
|
||||
ls -lh output/eeschema.wasm
|
||||
cp output/eeschema.wasm output/eeschema.asyncified.wasm
|
||||
|
||||
- name: Upload fixture artifact
|
||||
if: steps.params.outputs.build_fixture == 'true'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: o2-fixture
|
||||
path: output/eeschema.asyncified.wasm
|
||||
retention-days: 7
|
||||
|
||||
- name: Download cached fixture
|
||||
if: steps.params.outputs.build_fixture == 'false'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: o2-fixture
|
||||
path: output
|
||||
run-id: ${{ env.FIXTURE_RUN_ID }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# ---- The sweep ----
|
||||
- name: Run -O2 config sweep
|
||||
run: |
|
||||
FIX="output/eeschema.asyncified.wasm"
|
||||
[ -f "$FIX" ] || FIX="output/eeschema.wasm"
|
||||
ls -lh "$FIX"
|
||||
CONFIGS="${CONFIGS}" CORES="${CORES}" DIAGNOSTIC="${DIAGNOSTIC}" CAP_SECONDS="${CAP_SECONDS}" \
|
||||
./scripts/bench/o2-config-sweep.sh "$FIX"
|
||||
|
||||
- name: Upload bench results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: o2-bench-results-${{ github.run_id }}
|
||||
path: bench/o2-results/**
|
||||
if-no-files-found: warn
|
||||
|
||||
delete-runner:
|
||||
name: Delete Hetzner runner
|
||||
needs: [create-runner, bench]
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
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