# 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) 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. run: xvfb-run -a 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 }}