From b3b448dd169f8db092dc81aa9e0151a204351ade Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 18 Oct 2026 00:35:32 +0000 Subject: [PATCH 1/6] ci: migrate GitHub Actions jobs to self-hosted Mac mini runners Point first-party jobs at [self-hosted, macOS, ARM64], make workflow scripts BSD-portable (no apt/sudo, scratch files in $RUNNER_TEMP), drop Playwright --with-deps, and document runner prerequisites. Leave Google's osv-scanner reusable workflow on GitHub-hosted Ubuntu; that callee pins its own runner and Linux container. Co-authored-by: Joe Blau --- .github/workflows/code_quality.yml | 2 +- .github/workflows/coverage.yml | 6 ++- .github/workflows/docs.yml | 57 ++++++++++++++++++------- .github/workflows/perf.yml | 8 ++-- .github/workflows/publish_npm.yml | 21 ++++----- .github/workflows/security.yml | 4 ++ .github/workflows/tests_offline.yml | 6 +-- .github/workflows/tests_online.yml | 6 ++- docs/ci-runners.md | 66 +++++++++++++++++++++++++++++ 9 files changed, 138 insertions(+), 38 deletions(-) create mode 100644 docs/ci-runners.md diff --git a/.github/workflows/code_quality.yml b/.github/workflows/code_quality.yml index 7bc4dd76..a436d190 100644 --- a/.github/workflows/code_quality.yml +++ b/.github/workflows/code_quality.yml @@ -9,7 +9,7 @@ permissions: jobs: quality: - runs-on: ubuntu-latest + runs-on: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 7bcbc47f..1460e7c9 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -10,7 +10,7 @@ permissions: jobs: coverage: - runs-on: ubuntu-latest + runs-on: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 @@ -30,7 +30,9 @@ jobs: bun test tests/ --coverage --coverage-reporter=lcov --coverage-dir=coverage test -s coverage/lcov.info # Bun writes SDK-relative source paths; coverage services need repository-relative paths. - sed -i 's|^SF:|SF:packages/hyperliquid/|' coverage/lcov.info + # Rewrite via $RUNNER_TEMP: BSD sed has no GNU `sed -i` in-place form. + sed 's|^SF:|SF:packages/hyperliquid/|' coverage/lcov.info > "$RUNNER_TEMP/lcov.info" + mv "$RUNNER_TEMP/lcov.info" coverage/lcov.info - name: Upload coverage reports to Codecov uses: codecov/codecov-action@0fb7174895f61a3b6b78fc075e0cd60383518dac # v5 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 04942176..aff5c89c 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -40,7 +40,7 @@ concurrency: jobs: docs: - runs-on: ubuntu-latest + runs-on: [self-hosted, macOS, ARM64] timeout-minutes: 20 env: # Project Pages is served from /; PR builds use the same paths. @@ -72,28 +72,53 @@ jobs: working-directory: apps/docs run: bun run build + # `--with-deps` is apt/sudo on Linux. Chromium's macOS deps ship with the OS. - name: Install browser for documentation checks working-directory: apps/docs - run: bunx playwright install --with-deps chromium + run: bunx playwright install chromium - name: Test production documentation interactions working-directory: apps/docs run: bun run test:browser + # lychee-action downloads only `*-unknown-linux-gnu` and uses GNU `install -t`. + # Same lychee v0.24.2 scan, installed into $RUNNER_TEMP for the Mac mini. - name: Check documentation links - uses: lycheeverse/lychee-action@e7477775783ea5526144ba13e8db5eec57747ce8 # v2 - with: - args: >- - --root-dir . - --no-progress - --max-retries 3 - --timeout 20 - README.md - packages/hyperliquid/README.md - 'apps/docs/content/docs/**/*.md' - fail: true - failIfEmpty: true - token: ${{ secrets.GITHUB_TOKEN }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -eu + LYCHEE_VERSION=v0.24.2 + ARCHIVE=lychee-aarch64-apple-darwin.tar.gz + EXPECTED_SHA256=c9d3740ea2d891854d37116c9fba840f37b6e7c89d330e7db84ac333631c4977 + DEST="$RUNNER_TEMP/lychee" + mkdir -p "$DEST" + curl -fsSL "https://github.com/lycheeverse/lychee/releases/download/lychee-${LYCHEE_VERSION}/${ARCHIVE}" \ + -o "$DEST/$ARCHIVE" + actual=$(shasum -a 256 "$DEST/$ARCHIVE") + actual=${actual%% *} + if [ "$actual" != "$EXPECTED_SHA256" ]; then + echo "lychee archive checksum mismatch: got $actual want $EXPECTED_SHA256" >&2 + exit 1 + fi + tar -xzf "$DEST/$ARCHIVE" -C "$DEST" + LYCHEE="$DEST/lychee-aarch64-apple-darwin/lychee" + chmod +x "$LYCHEE" + report="$RUNNER_TEMP/lychee-out.md" + "$LYCHEE" \ + --root-dir . \ + --no-progress \ + --max-retries 3 \ + --timeout 20 \ + --format markdown \ + README.md \ + packages/hyperliquid/README.md \ + "apps/docs/content/docs/**/*.md" \ + | tee "$report" + if grep -E 'Total[[:space:]]+\|[[:space:]]+0' "$report" >/dev/null; then + echo "No links were found. This usually indicates a configuration error." >&2 + exit 1 + fi - name: Upload GitHub Pages artifact if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' @@ -104,7 +129,7 @@ jobs: deploy: if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' needs: docs - runs-on: ubuntu-latest + runs-on: [self-hosted, macOS, ARM64] timeout-minutes: 10 permissions: pages: write diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml index b89c6cec..672ac5fb 100644 --- a/.github/workflows/perf.yml +++ b/.github/workflows/perf.yml @@ -12,9 +12,9 @@ jobs: # improve it, never regress it. # # WHY THIS MEASURES TWICE INSTEAD OF USING THE SDK'S tests/perf/results/baseline.json: - # the scenarios measure between 9 ns and 600 µs per unit. A GitHub-hosted runner is several times - # slower than a developer machine, so comparing a committed baseline (recorded on whatever machine - # last touched it) against a runner would flag nearly every scenario as a regression on every PR. + # the scenarios measure between 9 ns and 600 µs per unit. A CI runner (even this Mac mini) is + # not the machine that last recorded the committed baseline, so comparing that file against a + # runner would flag nearly every scenario as a regression on every PR. # The SDK's `.dev/perf/compare.ts` refuses such a comparison outright. So this job measures the base # commit and the head commit back to back on the SAME runner and compares those two reports. # The committed baseline stays useful for local runs (`bun run perf:gate`) on one machine. @@ -35,7 +35,7 @@ jobs: # changes the workload being compared and is flagged for explicit review, not waved through. # If this job flaps, widen the threshold — do not add retries and do not drop the job. perf: - runs-on: ubuntu-latest + runs-on: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 diff --git a/.github/workflows/publish_npm.yml b/.github/workflows/publish_npm.yml index 868b10bc..bdb8e41a 100644 --- a/.github/workflows/publish_npm.yml +++ b/.github/workflows/publish_npm.yml @@ -6,7 +6,7 @@ on: jobs: publish: - runs-on: ubuntu-latest + runs-on: [self-hosted, macOS, ARM64] permissions: contents: read @@ -34,15 +34,16 @@ jobs: id: tag run: | # The private workspace root has no release version; the SDK owns the published version. - VERSION=$(jq -r .version packages/hyperliquid/package.json) - # Extract tag (alpha, beta, rc) from version string if present - TAG=$(echo "$VERSION" | sed -n 's/.*-\(alpha\|beta\|rc\).*/\1/p') - # If no known tag found but version contains hyphen, use "prerelease" - if [[ -z "$TAG" && "$VERSION" == *-* ]]; then - TAG="prerelease" - fi - # Default to "latest" if no tag determined - echo "value=${TAG:-latest}" >> "$GITHUB_OUTPUT" + # bun instead of jq; case instead of GNU sed `\|` / bash `[[`. + VERSION=$(bun -e 'console.log(JSON.parse(await Bun.file("packages/hyperliquid/package.json").text()).version)') + case "$VERSION" in + *-alpha*) TAG=alpha ;; + *-beta*) TAG=beta ;; + *-rc*) TAG=rc ;; + *-*) TAG=prerelease ;; + *) TAG=latest ;; + esac + echo "value=$TAG" >> "$GITHUB_OUTPUT" - name: Publish package working-directory: ./packages/hyperliquid/dist diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 4d659d5f..f122ef7d 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -19,6 +19,10 @@ jobs: actions: read contents: read security-events: write + # Not migrated to the Mac mini runners. This reusable workflow pins + # `runs-on: ubuntu-latest` and a Linux container internally; we cannot set + # its runner. See docs/ci-runners.md. + # # SHA is the v2.3.8 tag target, verified via `gh api repos/google/osv-scanner-action/git/ref/tags/v2.3.8`. # The reusable workflow pins its own sub-actions and the osv-scanner container image by SHA internally. uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@9a498708959aeaef5ef730655706c5a1df1edbc2 # v2.3.8 diff --git a/.github/workflows/tests_offline.yml b/.github/workflows/tests_offline.yml index 89059891..28b5ec70 100644 --- a/.github/workflows/tests_offline.yml +++ b/.github/workflows/tests_offline.yml @@ -8,7 +8,7 @@ permissions: jobs: test: - runs-on: ubuntu-latest + runs-on: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 @@ -25,7 +25,7 @@ jobs: # # Inside the SDK, `bun test tests/ -- --offline` also works: tests/_offline.ts recovers the raw command line # because `bun test` does not forward argv to test files. The env var is preferred here because - # it needs no such recovery — that path reads /proc/self/cmdline on Linux and is exercised only - # in CI, so relying on it would put the least-tested branch on the critical path. + # it needs no such recovery — that path reads /proc/self/cmdline on Linux or `ps` on Darwin + # and is the least-tested branch, so it stays off the CI critical path. - name: bun test (offline) run: bun run test:offline diff --git a/.github/workflows/tests_online.yml b/.github/workflows/tests_online.yml index b9e5f9ed..ebb1d47e 100644 --- a/.github/workflows/tests_online.yml +++ b/.github/workflows/tests_online.yml @@ -8,7 +8,7 @@ permissions: jobs: test: - runs-on: ubuntu-latest + runs-on: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 @@ -29,7 +29,9 @@ jobs: bun test tests/ --coverage --coverage-reporter=lcov --coverage-dir=coverage test -s coverage/lcov.info # Bun writes SDK-relative source paths; coverage services need repository-relative paths. - sed -i 's|^SF:|SF:packages/hyperliquid/|' coverage/lcov.info + # Rewrite via $RUNNER_TEMP: BSD sed has no GNU `sed -i` in-place form. + sed 's|^SF:|SF:packages/hyperliquid/|' coverage/lcov.info > "$RUNNER_TEMP/lcov.info" + mv "$RUNNER_TEMP/lcov.info" coverage/lcov.info - name: Upload coverage reports to Codecov uses: codecov/codecov-action@0fb7174895f61a3b6b78fc075e0cd60383518dac # v5 diff --git a/docs/ci-runners.md b/docs/ci-runners.md new file mode 100644 index 00000000..28db09d4 --- /dev/null +++ b/docs/ci-runners.md @@ -0,0 +1,66 @@ +# CI runners + +Jobs in this repository run on self-hosted Apple Silicon Mac minis. + +```yaml +runs-on: [self-hosted, macOS, ARM64] +``` + +The runner must carry those three labels exactly. Workflows do not use `apt`, `sudo`, Homebrew, or Docker. + +## Not migrated + +`security.yml` calls Google's `osv-scanner-reusable.yml`. That reusable workflow pins `runs-on: ubuntu-latest` and a Linux container internally. This repository cannot set its runner, so the OSV scan stays on GitHub-hosted Ubuntu. + +## What the runner must already have + +Install these on the Mac mini before registering the GitHub Actions runner. Workflows never elevate privileges. + +| Tool | Why | +| --- | --- | +| macOS on Apple Silicon | Matches the `macOS` and `ARM64` labels. | +| GitHub Actions runner | Registered with labels `self-hosted`, `macOS`, `ARM64`. | +| Xcode Command Line Tools | `git`, `clang`, and the headers native packages (Bun, sharp, Playwright) expect. | +| `curl`, `tar`, `sed`, `shasum`, `bash`, `tee` | Stock macOS. Workflow scripts stay BSD-portable and write scratch files under `$RUNNER_TEMP`. | + +Do not install CI tools with `apt` or `sudo` for these workflows. Homebrew is not required. + +The runner user must be able to write the workspace and `$RUNNER_TEMP`. The machine needs enough free disk for a Bun install, a perf worktree, and Playwright's Chromium download (a few gigabytes in total). + +## What workflows install themselves + +These are not machine prerequisites. The jobs fetch them into the job environment or `$RUNNER_TEMP`: + +- **Bun canary** — `oven-sh/setup-bun` on every job that runs workspace scripts. +- **Node.js 24** — `actions/setup-node` on the npm publish job only (OIDC trusted publishing needs npm ≥ 11.5.1). +- **Playwright Chromium** — `bunx playwright install chromium` in the docs job. `--with-deps` is omitted; that flag installs Linux apt packages. Chromium's macOS libraries ship with the OS. The first docs run downloads the browser into Playwright's cache on the runner. +- **lychee v0.24.2** (`aarch64-apple-darwin`) — downloaded into `$RUNNER_TEMP`, checksum-verified, and used for the same Markdown link scan as before. The `lychee-action` composite only ships a Linux GNU binary. + +## Network egress + +The runner needs outbound HTTPS to: + +- `github.com` (checkout, Actions APIs, Pages deploy, lychee release assets, OIDC) +- the Bun and npm registries +- Playwright's browser CDN +- `codecov.io` and `coveralls.io` (coverage uploads) +- Hyperliquid testnet (the online test job) +- `registry.npmjs.org` (publish) + +## Secrets + +Unchanged: `CODECOV_TOKEN`, `PRIVATE_KEY` (online tests), and the default `GITHUB_TOKEN`. npm publish uses OIDC trusted publishing (`id-token: write`), not an npm token. + +## Jobs + +| Workflow | Job | Runner | +| --- | --- | --- | +| `code_quality.yml` | `quality` | self-hosted Mac mini | +| `coverage.yml` | `coverage` | self-hosted Mac mini | +| `docs.yml` | `docs` | self-hosted Mac mini | +| `docs.yml` | `deploy` | self-hosted Mac mini | +| `perf.yml` | `perf` | self-hosted Mac mini | +| `publish_npm.yml` | `publish` | self-hosted Mac mini | +| `tests_offline.yml` | `test` | self-hosted Mac mini | +| `tests_online.yml` | `test` | self-hosted Mac mini | +| `security.yml` | `osv-scan` | GitHub-hosted Ubuntu (Google reusable workflow) | From b71ef237a5a8a304f049c0b3ac3cc03d422ca13d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 18 Oct 2026 00:56:08 +0000 Subject: [PATCH 2/6] ci: export TMPDIR from RUNNER_TEMP and run coverage on PRs Job-level env cannot use the runner context on these Mac minis, so set TMPDIR=$RUNNER_TEMP via GITHUB_ENV in a setup step. Run coverage on pull requests so that Mac mini job is verified with the rest of CI. Co-authored-by: Joe Blau --- .github/workflows/code_quality.yml | 3 +++ .github/workflows/coverage.yml | 4 ++++ .github/workflows/docs.yml | 6 ++++++ .github/workflows/perf.yml | 3 +++ .github/workflows/publish_npm.yml | 3 +++ .github/workflows/tests_offline.yml | 3 +++ .github/workflows/tests_online.yml | 3 +++ docs/ci-runners.md | 2 +- 8 files changed, 26 insertions(+), 1 deletion(-) diff --git a/.github/workflows/code_quality.yml b/.github/workflows/code_quality.yml index a436d190..964cf63c 100644 --- a/.github/workflows/code_quality.yml +++ b/.github/workflows/code_quality.yml @@ -12,6 +12,9 @@ jobs: runs-on: [self-hosted, macOS, ARM64] steps: + # Job-level env cannot use the runner context on these Mac minis. + - name: Point TMPDIR at runner temp + run: echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 with: diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 1460e7c9..13d7e599 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,6 +1,7 @@ name: Coverage on: + pull_request: push: branches: [main] workflow_dispatch: @@ -13,6 +14,9 @@ jobs: runs-on: [self-hosted, macOS, ARM64] steps: + # Job-level env cannot use the runner context on these Mac minis. + - name: Point TMPDIR at runner temp + run: echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 with: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index aff5c89c..8dddefa0 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -48,6 +48,9 @@ jobs: NEXT_TELEMETRY_DISABLED: "1" steps: + # Job-level env cannot use the runner context on these Mac minis. + - name: Point TMPDIR at runner temp + run: echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 with: @@ -141,6 +144,9 @@ jobs: group: github-pages cancel-in-progress: false steps: + # Job-level env cannot use the runner context on these Mac minis. + - name: Point TMPDIR at runner temp + run: echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" # Enable Settings > Pages > Source: GitHub Actions before the first deployment. # Keeping Pages API calls here lets PR checks run before Pages is enabled. - name: Configure GitHub Pages diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml index 672ac5fb..6770cc5d 100644 --- a/.github/workflows/perf.yml +++ b/.github/workflows/perf.yml @@ -38,6 +38,9 @@ jobs: runs-on: [self-hosted, macOS, ARM64] steps: + # Job-level env cannot use the runner context on these Mac minis. + - name: Point TMPDIR at runner temp + run: echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: # The base commit must be reachable so it can be checked out into a second worktree. diff --git a/.github/workflows/publish_npm.yml b/.github/workflows/publish_npm.yml index bdb8e41a..7600728e 100644 --- a/.github/workflows/publish_npm.yml +++ b/.github/workflows/publish_npm.yml @@ -13,6 +13,9 @@ jobs: id-token: write steps: + # Job-level env cannot use the runner context on these Mac minis. + - name: Point TMPDIR at runner temp + run: echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 # npm publishes via OIDC trusted publishing (no token here), which needs npm >= 11.5.1. - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 diff --git a/.github/workflows/tests_offline.yml b/.github/workflows/tests_offline.yml index 28b5ec70..38d0f90c 100644 --- a/.github/workflows/tests_offline.yml +++ b/.github/workflows/tests_offline.yml @@ -11,6 +11,9 @@ jobs: runs-on: [self-hosted, macOS, ARM64] steps: + # Job-level env cannot use the runner context on these Mac minis. + - name: Point TMPDIR at runner temp + run: echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 with: diff --git a/.github/workflows/tests_online.yml b/.github/workflows/tests_online.yml index ebb1d47e..9beb92d2 100644 --- a/.github/workflows/tests_online.yml +++ b/.github/workflows/tests_online.yml @@ -11,6 +11,9 @@ jobs: runs-on: [self-hosted, macOS, ARM64] steps: + # Job-level env cannot use the runner context on these Mac minis. + - name: Point TMPDIR at runner temp + run: echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 with: diff --git a/docs/ci-runners.md b/docs/ci-runners.md index 28db09d4..d09aa206 100644 --- a/docs/ci-runners.md +++ b/docs/ci-runners.md @@ -21,7 +21,7 @@ Install these on the Mac mini before registering the GitHub Actions runner. Work | macOS on Apple Silicon | Matches the `macOS` and `ARM64` labels. | | GitHub Actions runner | Registered with labels `self-hosted`, `macOS`, `ARM64`. | | Xcode Command Line Tools | `git`, `clang`, and the headers native packages (Bun, sharp, Playwright) expect. | -| `curl`, `tar`, `sed`, `shasum`, `bash`, `tee` | Stock macOS. Workflow scripts stay BSD-portable and write scratch files under `$RUNNER_TEMP`. | +| `curl`, `tar`, `sed`, `shasum`, `bash`, `tee` | Stock macOS. Workflow scripts stay BSD-portable and write scratch files under `$RUNNER_TEMP` (exported as `TMPDIR` via `$GITHUB_ENV` in a setup step; job-level `env` cannot use the `runner` context). | Do not install CI tools with `apt` or `sudo` for these workflows. Homebrew is not required. From f426c76b70bb236b634d01d1a97ef4f8098ccf9b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 18 Oct 2026 00:59:36 +0000 Subject: [PATCH 3/6] ci: pin-download Codecov CLI; Mac mini has no gpg codecov-action refuses to start without host gpg. Download the official universal macOS CLI (v11.3.1), SHA256-verify it, and upload with the same --file/--disable-search/--fail-on-error flags. Do not skip the upload. Co-authored-by: Joe Blau --- .github/scripts/codecov-upload.sh | 29 +++++++++++++++++++++++++++++ .github/workflows/coverage.yml | 11 +++++------ .github/workflows/tests_online.yml | 11 +++++------ docs/ci-runners.md | 3 +++ 4 files changed, 42 insertions(+), 12 deletions(-) create mode 100755 .github/scripts/codecov-upload.sh diff --git a/.github/scripts/codecov-upload.sh b/.github/scripts/codecov-upload.sh new file mode 100755 index 00000000..56a0d19e --- /dev/null +++ b/.github/scripts/codecov-upload.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# Pin-download the official macOS Codecov CLI and upload one lcov file. +# codecov-action requires host `gpg` to verify its downloaded CLI; these Mac minis +# do not have gpg. SHA256 of codecovcli_macos v11.3.1 matches cli.codecov.io. +set -eu + +FILE=$1 +VERSION=v11.3.1 +EXPECTED_SHA256=ffb2b821551076b69d3267508b2d5dc10925c821c436521f2216f5ead62cb138 +DEST="$RUNNER_TEMP/codecov" +BIN="$DEST/codecov" + +mkdir -p "$DEST" +curl -fsSL "https://github.com/codecov/codecov-cli/releases/download/${VERSION}/codecovcli_macos" \ + -o "$BIN" +actual=$(shasum -a 256 "$BIN") +actual=${actual%% *} +if [ "$actual" != "$EXPECTED_SHA256" ]; then + echo "codecov CLI checksum mismatch: got $actual want $EXPECTED_SHA256" >&2 + exit 1 +fi +chmod +x "$BIN" + +"$BIN" upload-coverage \ + -t "$CODECOV_TOKEN" \ + --file "$FILE" \ + --disable-search \ + --fail-on-error \ + --git-service github diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 13d7e599..444e4629 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -38,10 +38,9 @@ jobs: sed 's|^SF:|SF:packages/hyperliquid/|' coverage/lcov.info > "$RUNNER_TEMP/lcov.info" mv "$RUNNER_TEMP/lcov.info" coverage/lcov.info + # codecov-action requires host `gpg` for CLI integrity. These Mac minis do + # not have it; same pinned CLI + SHA256 check as lychee, same upload flags. - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@0fb7174895f61a3b6b78fc075e0cd60383518dac # v5 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./packages/hyperliquid/coverage/lcov.info - disable_search: true - fail_ci_if_error: true + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + run: sh .github/scripts/codecov-upload.sh ./packages/hyperliquid/coverage/lcov.info diff --git a/.github/workflows/tests_online.yml b/.github/workflows/tests_online.yml index 9beb92d2..9f77a34f 100644 --- a/.github/workflows/tests_online.yml +++ b/.github/workflows/tests_online.yml @@ -36,13 +36,12 @@ jobs: sed 's|^SF:|SF:packages/hyperliquid/|' coverage/lcov.info > "$RUNNER_TEMP/lcov.info" mv "$RUNNER_TEMP/lcov.info" coverage/lcov.info + # codecov-action requires host `gpg` for CLI integrity. These Mac minis do + # not have it; same pinned CLI + SHA256 check as lychee, same upload flags. - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@0fb7174895f61a3b6b78fc075e0cd60383518dac # v5 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./packages/hyperliquid/coverage/lcov.info - disable_search: true - fail_ci_if_error: true + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + run: sh .github/scripts/codecov-upload.sh ./packages/hyperliquid/coverage/lcov.info - name: Upload coverage to Coveralls.io uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2 diff --git a/docs/ci-runners.md b/docs/ci-runners.md index d09aa206..20fdd1fa 100644 --- a/docs/ci-runners.md +++ b/docs/ci-runners.md @@ -25,6 +25,8 @@ Install these on the Mac mini before registering the GitHub Actions runner. Work Do not install CI tools with `apt` or `sudo` for these workflows. Homebrew is not required. +The runner currently lacks **`gpg`**. `codecov/codecov-action` refuses to start without it (it GPG-checks the Codecov CLI). These workflows do not need host `gpg`: they pin-download `codecovcli_macos` v11.3.1 and SHA256-verify it, the same way they install lychee. Sibling repos that still call `codecov-action` directly will need `gpg` on the Mac mini (GnuPG, on `PATH`). This repository also does not need host `gh`, `gtar`, or `jq`. + The runner user must be able to write the workspace and `$RUNNER_TEMP`. The machine needs enough free disk for a Bun install, a perf worktree, and Playwright's Chromium download (a few gigabytes in total). ## What workflows install themselves @@ -35,6 +37,7 @@ These are not machine prerequisites. The jobs fetch them into the job environmen - **Node.js 24** — `actions/setup-node` on the npm publish job only (OIDC trusted publishing needs npm ≥ 11.5.1). - **Playwright Chromium** — `bunx playwright install chromium` in the docs job. `--with-deps` is omitted; that flag installs Linux apt packages. Chromium's macOS libraries ship with the OS. The first docs run downloads the browser into Playwright's cache on the runner. - **lychee v0.24.2** (`aarch64-apple-darwin`) — downloaded into `$RUNNER_TEMP`, checksum-verified, and used for the same Markdown link scan as before. The `lychee-action` composite only ships a Linux GNU binary. +- **Codecov CLI v11.3.1** (`codecovcli_macos`, universal x86_64/arm64) — downloaded into `$RUNNER_TEMP`, checksum-verified, and used for the same `upload-coverage` call as `codecov-action` (`--file`, `--disable-search`, `--fail-on-error`). The action is not used because it requires host `gpg`. ## Network egress From 63e36611ea36a0ce1a37825e79868deb25518f8e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 18 Oct 2026 01:02:37 +0000 Subject: [PATCH 4/6] ci: install Bun canary into RUNNER_TEMP per job setup-bun writes ~/.bun, which is shared across runner instances on the same Mac mini. A parallel job overwrote canary mid-run (1.4.4 then 1.4.0), and the perf gate refused the environment mismatch. Isolate the binary. Co-authored-by: Joe Blau --- .github/scripts/setup-bun-isolated.sh | 22 ++++++++++++++++++++++ .github/workflows/code_quality.yml | 7 +++---- .github/workflows/coverage.yml | 7 +++---- .github/workflows/docs.yml | 7 +++---- .github/workflows/perf.yml | 9 +++++---- .github/workflows/publish_npm.yml | 7 +++---- .github/workflows/tests_offline.yml | 7 +++---- .github/workflows/tests_online.yml | 7 +++---- docs/ci-runners.md | 4 ++-- 9 files changed, 47 insertions(+), 30 deletions(-) create mode 100755 .github/scripts/setup-bun-isolated.sh diff --git a/.github/scripts/setup-bun-isolated.sh b/.github/scripts/setup-bun-isolated.sh new file mode 100755 index 00000000..d5d2f6f3 --- /dev/null +++ b/.github/scripts/setup-bun-isolated.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# Install Bun canary into $RUNNER_TEMP so parallel jobs on the same Mac mini +# do not overwrite a shared ~/.bun (oven-sh/setup-bun's default). The perf +# gate records Bun.version per revision; a mid-job overwrite looks like an +# environment mismatch and fails the compare. +set -eu + +DEST="$RUNNER_TEMP/bun" +mkdir -p "$DEST" +curl -fsSL "https://github.com/oven-sh/bun/releases/download/canary/bun-darwin-aarch64.zip" \ + -o "$DEST/bun.zip" +unzip -o -q "$DEST/bun.zip" -d "$DEST" +BIN="$DEST/bun-darwin-aarch64/bun" +if [ ! -f "$BIN" ]; then + echo "bun binary missing after extract" >&2 + exit 1 +fi +chmod +x "$BIN" +# Isolate bun's own home (install cache, etc.) from the shared ~/.bun. +echo "BUN_INSTALL=$DEST/home" >> "$GITHUB_ENV" +echo "$(dirname "$BIN")" >> "$GITHUB_PATH" +"$BIN" --revision diff --git a/.github/workflows/code_quality.yml b/.github/workflows/code_quality.yml index 964cf63c..2ee40944 100644 --- a/.github/workflows/code_quality.yml +++ b/.github/workflows/code_quality.yml @@ -16,10 +16,9 @@ jobs: - name: Point TMPDIR at runner temp run: echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 - with: - # Bun 1.4 (the Rust rewrite) ships only as canary; the `canary` tag moves with each build. - bun-version: canary + # setup-bun writes ~/.bun, shared across runner instances on one Mac mini. + - name: Install Bun canary into runner temp + run: sh .github/scripts/setup-bun-isolated.sh - name: Install dependencies run: bun install --frozen-lockfile diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 444e4629..73f7ed10 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -18,10 +18,9 @@ jobs: - name: Point TMPDIR at runner temp run: echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 - with: - # Match the rest of CI while Bun 1.4 remains canary-only. - bun-version: canary + # setup-bun writes ~/.bun, shared across runner instances on one Mac mini. + - name: Install Bun canary into runner temp + run: sh .github/scripts/setup-bun-isolated.sh - name: Install dependencies run: bun install --frozen-lockfile diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 8dddefa0..2bd07ea0 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -52,10 +52,9 @@ jobs: - name: Point TMPDIR at runner temp run: echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 - with: - # Match the rest of CI while Bun 1.4 remains canary-only. - bun-version: canary + # setup-bun writes ~/.bun, shared across runner instances on one Mac mini. + - name: Install Bun canary into runner temp + run: sh .github/scripts/setup-bun-isolated.sh - name: Install dependencies run: bun install --frozen-lockfile diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml index 6770cc5d..c592337e 100644 --- a/.github/workflows/perf.yml +++ b/.github/workflows/perf.yml @@ -46,10 +46,11 @@ jobs: # The base commit must be reachable so it can be checked out into a second worktree. fetch-depth: 0 - - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 - with: - # Bun 1.4 (the Rust rewrite) ships only as canary; the `canary` tag moves with each build. - bun-version: canary + # setup-bun writes ~/.bun, shared across runner instances on one Mac mini. + # Parallel canary installs overwrite each other; the perf compare then + # sees two Bun versions in one job and refuses the comparison. + - name: Install Bun canary into runner temp + run: sh .github/scripts/setup-bun-isolated.sh - name: Install dependencies run: bun install --frozen-lockfile diff --git a/.github/workflows/publish_npm.yml b/.github/workflows/publish_npm.yml index 7600728e..57ea40c3 100644 --- a/.github/workflows/publish_npm.yml +++ b/.github/workflows/publish_npm.yml @@ -21,10 +21,9 @@ jobs: - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 with: node-version: "24.x" - - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 - with: - # Bun 1.4 (the Rust rewrite) ships only as canary; the `canary` tag moves with each build. - bun-version: canary + # setup-bun writes ~/.bun, shared across runner instances on one Mac mini. + - name: Install Bun canary into runner temp + run: sh .github/scripts/setup-bun-isolated.sh - name: Install dependencies run: bun install --frozen-lockfile diff --git a/.github/workflows/tests_offline.yml b/.github/workflows/tests_offline.yml index 38d0f90c..cc93aa13 100644 --- a/.github/workflows/tests_offline.yml +++ b/.github/workflows/tests_offline.yml @@ -15,10 +15,9 @@ jobs: - name: Point TMPDIR at runner temp run: echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 - with: - # Bun 1.4 (the Rust rewrite) ships only as canary; the `canary` tag moves with each build. - bun-version: canary + # setup-bun writes ~/.bun, shared across runner instances on one Mac mini. + - name: Install Bun canary into runner temp + run: sh .github/scripts/setup-bun-isolated.sh - name: Install dependencies run: bun install --frozen-lockfile diff --git a/.github/workflows/tests_online.yml b/.github/workflows/tests_online.yml index 9f77a34f..9dbebeaf 100644 --- a/.github/workflows/tests_online.yml +++ b/.github/workflows/tests_online.yml @@ -15,10 +15,9 @@ jobs: - name: Point TMPDIR at runner temp run: echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 - with: - # Bun 1.4 (the Rust rewrite) ships only as canary; the `canary` tag moves with each build. - bun-version: canary + # setup-bun writes ~/.bun, shared across runner instances on one Mac mini. + - name: Install Bun canary into runner temp + run: sh .github/scripts/setup-bun-isolated.sh - name: Install dependencies run: bun install --frozen-lockfile diff --git a/docs/ci-runners.md b/docs/ci-runners.md index 20fdd1fa..2daf4bf9 100644 --- a/docs/ci-runners.md +++ b/docs/ci-runners.md @@ -21,7 +21,7 @@ Install these on the Mac mini before registering the GitHub Actions runner. Work | macOS on Apple Silicon | Matches the `macOS` and `ARM64` labels. | | GitHub Actions runner | Registered with labels `self-hosted`, `macOS`, `ARM64`. | | Xcode Command Line Tools | `git`, `clang`, and the headers native packages (Bun, sharp, Playwright) expect. | -| `curl`, `tar`, `sed`, `shasum`, `bash`, `tee` | Stock macOS. Workflow scripts stay BSD-portable and write scratch files under `$RUNNER_TEMP` (exported as `TMPDIR` via `$GITHUB_ENV` in a setup step; job-level `env` cannot use the `runner` context). | +| `curl`, `tar`, `unzip`, `sed`, `shasum`, `bash`, `tee` | Stock macOS. Workflow scripts stay BSD-portable and write scratch files under `$RUNNER_TEMP` (exported as `TMPDIR` via `$GITHUB_ENV` in a setup step; job-level `env` cannot use the `runner` context). | Do not install CI tools with `apt` or `sudo` for these workflows. Homebrew is not required. @@ -33,7 +33,7 @@ The runner user must be able to write the workspace and `$RUNNER_TEMP`. The mach These are not machine prerequisites. The jobs fetch them into the job environment or `$RUNNER_TEMP`: -- **Bun canary** — `oven-sh/setup-bun` on every job that runs workspace scripts. +- **Bun canary** — downloaded into `$RUNNER_TEMP` (`bun-darwin-aarch64` zip) on every job that runs workspace scripts. `oven-sh/setup-bun` is not used: it installs into `~/.bun`, which is shared across runner instances on the same Mac mini, and a mid-job overwrite makes the perf gate see two Bun versions. - **Node.js 24** — `actions/setup-node` on the npm publish job only (OIDC trusted publishing needs npm ≥ 11.5.1). - **Playwright Chromium** — `bunx playwright install chromium` in the docs job. `--with-deps` is omitted; that flag installs Linux apt packages. Chromium's macOS libraries ship with the OS. The first docs run downloads the browser into Playwright's cache on the runner. - **lychee v0.24.2** (`aarch64-apple-darwin`) — downloaded into `$RUNNER_TEMP`, checksum-verified, and used for the same Markdown link scan as before. The `lychee-action` composite only ships a Linux GNU binary. From 906e8eea9e33880d3ee1409f89a5e1e88b60b74e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 18 Oct 2026 01:04:59 +0000 Subject: [PATCH 5/6] ci: symlink bunx next to the isolated Bun binary The canary zip ships only bun. docs runs bunx playwright install chromium and failed with command not found. Co-authored-by: Joe Blau --- .github/scripts/setup-bun-isolated.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/scripts/setup-bun-isolated.sh b/.github/scripts/setup-bun-isolated.sh index d5d2f6f3..d7f1debb 100755 --- a/.github/scripts/setup-bun-isolated.sh +++ b/.github/scripts/setup-bun-isolated.sh @@ -16,6 +16,8 @@ if [ ! -f "$BIN" ]; then exit 1 fi chmod +x "$BIN" +# The canary zip ships only `bun`. setup-bun also linked `bunx` (same binary). +ln -sf bun "$(dirname "$BIN")/bunx" # Isolate bun's own home (install cache, etc.) from the shared ~/.bun. echo "BUN_INSTALL=$DEST/home" >> "$GITHUB_ENV" echo "$(dirname "$BIN")" >> "$GITHUB_PATH" From f44356faf3997245168f3bd91d1caa39b090eb46 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 18 Oct 2026 01:07:51 +0000 Subject: [PATCH 6/6] ci: prune leftover git worktrees before the perf base checkout Self-hosted workspaces persist. A prior job left RUNNER_TEMP/base registered after the directory was wiped, so worktree add failed. Co-authored-by: Joe Blau --- .github/workflows/perf.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml index c592337e..3cbacc02 100644 --- a/.github/workflows/perf.yml +++ b/.github/workflows/perf.yml @@ -71,6 +71,11 @@ jobs: exit 1 } echo "Base: $BASE_REF -> $BASE_SHA" + # Self-hosted workspaces persist. A prior job can leave $RUNNER_TEMP/base + # registered after the directory was wiped; `worktree add` then fails. + git worktree prune + git worktree remove --force "$RUNNER_TEMP/base" 2>/dev/null || true + rm -rf "$RUNNER_TEMP/base" git worktree add "$RUNNER_TEMP/base" "$BASE_SHA" # Locate each revision independently: older commits keep the SDK at the repository # root, while workspace commits keep the same suite under packages/hyperliquid.