fix(workbench): hide disabled harnesses from new tasks #1572
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: | |
| - master | |
| - release/* | |
| pull_request: | |
| paths-ignore: | |
| - "**.md" | |
| workflow_dispatch: | |
| inputs: | |
| release_type: | |
| type: choice | |
| options: | |
| - Dry Run | |
| - Release | |
| # Runners resolve through repo/org `vars` (ArcBox pins paid Blacksmith labels there); | |
| # forks without the vars fall back to GitHub-hosted labels with zero setup. | |
| # Pull request and release-branch CI keeps latest-run cancellation. Every master SHA gets a unique | |
| # group so a release-PR merge cannot be canceled or replaced before its exact SHA is tested. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }}-${{ github.ref == 'refs/heads/master' && github.sha || 'latest' }} | |
| cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} | |
| permissions: | |
| contents: read | |
| env: | |
| # Non-lint Node steps still need more than the default 4096 MB heap; lint sets its own limit. | |
| NODE_OPTIONS: --max-old-space-size=6144 | |
| jobs: | |
| typescript: | |
| name: TypeScript | |
| runs-on: ${{ vars.CI_RUNNER_LINUX || 'ubuntu-latest' }} | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Setup Rust | |
| id: setup-rust | |
| uses: actions-rust-lang/setup-rust-toolchain@v1 | |
| with: | |
| rustflags: "" # keep build.rustflags; the -D warnings default belongs to the Rust job's clippy call | |
| background: true | |
| - parallel: | |
| - uses: pnpm/action-setup@v6 | |
| with: | |
| run_install: false | |
| cache: true | |
| - uses: actions/setup-node@v7.0.0 | |
| with: | |
| node-version-file: .nvmrc | |
| package-manager-cache: false | |
| # Before `pnpm install`, so the hashFiles globs below cannot reach into node_modules. | |
| # | |
| # eslint validates a cached entry against that file's own content only, so a cross-file | |
| # type change would leave an unchanged file's cached result in place — a type-aware rule | |
| # such as no-floating-promises can start applying to a caller nobody edited, and `tsc` | |
| # does not reject that code either. The key therefore covers every lint-visible input at | |
| # once and there are deliberately no restore-keys: a prefix match would reintroduce | |
| # exactly that hole. The cost is that this only hits on commits touching no lintable | |
| # source (docs, Rust, workflows); a run that changes any of them re-lints from cold, | |
| # which is the correct answer. | |
| - name: Restore ESLint cache | |
| uses: actions/cache@v6 | |
| with: | |
| path: .eslintcache | |
| key: eslint-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', 'package.json', 'apps/*/package.json', 'packages/*/*/package.json', 'tsconfig*.json', 'apps/*/tsconfig.json', 'apps/*/tests/tsconfig.json', 'apps/*/e2e/tsconfig.json', 'packages/*/*/tsconfig.json', 'packages/*/*/tests/tsconfig.json') }}-${{ hashFiles('packages/*/*/src/**') }}-${{ github.sha }} | |
| restore-keys: | | |
| eslint-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', 'package.json', 'apps/*/package.json', 'packages/*/*/package.json', 'tsconfig*.json', 'apps/*/tsconfig.json', 'apps/*/tests/tsconfig.json', 'apps/*/e2e/tsconfig.json', 'packages/*/*/tsconfig.json', 'packages/*/*/tests/tsconfig.json') }}-${{ hashFiles('packages/*/*/src/**') }}- | |
| eslint-${{ runner.os }}- | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| # After `pnpm install`, which could otherwise prune the restored node_modules/.cache dirs. | |
| # `tsc --build` validates staleness from .tsbuildinfo content hashes, not timestamps | |
| # (touching every source leaves a warm build sub-second), so restoring a stale build info | |
| # is always safe — tsc rechecks exactly what changed. restore-keys is therefore sound here, | |
| # unlike for the ESLint cache above. | |
| - name: Restore TypeScript build info | |
| uses: actions/cache@v6 | |
| with: | |
| path: | | |
| apps/*/node_modules/.cache/tsconfig*.tsbuildinfo | |
| packages/*/*/node_modules/.cache/tsconfig*.tsbuildinfo | |
| key: tsc-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml', 'tsconfig*.json', 'apps/*/tsconfig.json', 'apps/*/tests/tsconfig.json', 'apps/*/e2e/tsconfig.json', 'packages/*/*/tsconfig.json', 'packages/*/*/tests/tsconfig.json') }}-${{ github.sha }} | |
| restore-keys: | | |
| tsc-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml', 'tsconfig*.json', 'apps/*/tsconfig.json', 'apps/*/tests/tsconfig.json', 'apps/*/e2e/tsconfig.json', 'packages/*/*/tsconfig.json', 'packages/*/*/tests/tsconfig.json') }}- | |
| tsc-${{ runner.os }}- | |
| - wait: setup-rust | |
| - name: Build PTY sidecar for interoperability tests | |
| id: build-sidecar | |
| run: cargo build --locked -p linkcode-pty | |
| background: true | |
| - name: Check formatting and imports | |
| run: pnpm format:check | |
| # Type-aware programs are duplicated per worker, so lint defaults to single-threaded. | |
| - name: Lint | |
| run: pnpm lint | |
| - name: Typecheck | |
| run: pnpm typecheck | |
| - wait: build-sidecar | |
| - name: Test | |
| env: | |
| LINKCODE_PTY_SIDECAR_PATH: ${{ github.workspace }}/target/debug/linkcode-pty | |
| LINKCODE_REQUIRE_PTY_SIDECAR: "1" | |
| run: | | |
| pnpm test --exclude='apps/daemon/tests/integration/**' | |
| pnpm test apps/daemon/tests/integration --no-file-parallelism | |
| - name: Daemon process acceptance | |
| env: | |
| LINKCODE_PTY_SIDECAR_PATH: ${{ github.workspace }}/target/debug/linkcode-pty | |
| run: | | |
| pnpm -F @linkcode/daemon build | |
| pnpm -F @linkcode/daemon e2e:startup | |
| desktop: | |
| name: Desktop App Entry | |
| runs-on: ${{ vars.CI_RUNNER_LINUX || 'ubuntu-latest' }} | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Setup Rust | |
| id: setup-rust | |
| uses: actions-rust-lang/setup-rust-toolchain@v1 | |
| with: | |
| rustflags: "" | |
| background: true | |
| - parallel: | |
| - uses: pnpm/action-setup@v6 | |
| with: | |
| run_install: false | |
| cache: true | |
| - uses: actions/setup-node@v7.0.0 | |
| with: | |
| node-version-file: .nvmrc | |
| package-manager-cache: false | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Install Electron system dependencies | |
| run: | | |
| pnpm -F @linkcode/desktop exec playwright-core install-deps chromium | |
| sudo apt-get install --no-install-recommends -y openbox x11-utils | |
| - name: Test unpackaged app entry | |
| run: xvfb-run -a pnpm -F @linkcode/desktop e2e:unpackaged | |
| - name: Test window state persistence | |
| run: | | |
| xvfb-run -a sh -c ' | |
| openbox >/tmp/linkcode-openbox.log 2>&1 & | |
| wm_pid=$! | |
| trap "kill $wm_pid 2>/dev/null || true" EXIT | |
| # Maximize needs a managing WM; wait for the EWMH readiness marker instead | |
| # of racing openbox startup. | |
| for _ in $(seq 1 50); do | |
| xprop -root _NET_SUPPORTING_WM_CHECK 2>/dev/null | grep -q "window id" && break | |
| sleep 0.2 | |
| done | |
| pnpm -F @linkcode/desktop e2e:window-bounds | |
| ' | |
| - wait: setup-rust | |
| - name: Test unsigned packaged app entry | |
| run: xvfb-run -a pnpm -F @linkcode/desktop e2e:packaged | |
| webview: | |
| name: Webview Browser Entry | |
| runs-on: ${{ vars.CI_RUNNER_LINUX || 'ubuntu-latest' }} | |
| timeout-minutes: 20 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - parallel: | |
| - uses: pnpm/action-setup@v6 | |
| with: | |
| run_install: false | |
| cache: true | |
| - uses: actions/setup-node@v7.0.0 | |
| with: | |
| node-version-file: .nvmrc | |
| package-manager-cache: false | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Install Chromium | |
| run: pnpm -F @linkcode/webview exec playwright-core install --with-deps chromium --only-shell | |
| - name: Test production and mock browser entries | |
| run: pnpm -F @linkcode/webview e2e:browser | |
| # Never expose production telemetry configuration to pull-request code. The browser smoke | |
| # above deliberately runs without it; push/manual builds separately verify the real bundle. | |
| - name: Build production bundle with telemetry | |
| if: ${{ github.event_name != 'pull_request' }} | |
| env: | |
| VITE_SENTRY_DSN: ${{ secrets.SENTRY_DSN_WEBVIEW }} | |
| VITE_POSTHOG_PROJECT_TOKEN: ${{ secrets.POSTHOG_PROJECT_TOKEN }} | |
| VITE_POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }} | |
| run: pnpm -F @linkcode/webview build | |
| mobile: | |
| name: Mobile Native Bundles | |
| runs-on: ${{ vars.CI_RUNNER_LINUX || 'ubuntu-latest' }} | |
| timeout-minutes: 20 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - parallel: | |
| - uses: pnpm/action-setup@v6 | |
| with: | |
| run_install: false | |
| cache: true | |
| - uses: actions/setup-node@v7.0.0 | |
| with: | |
| node-version-file: .nvmrc | |
| package-manager-cache: false | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Export Android and iOS app entries | |
| # SENTRY_AUTH_TOKEN intentionally stays in EAS: ordinary CI does not upload source maps. | |
| env: | |
| EXPO_PUBLIC_SENTRY_DSN: ${{ github.event_name != 'pull_request' && secrets.SENTRY_DSN_MOBILE || '' }} | |
| EXPO_PUBLIC_POSTHOG_PROJECT_TOKEN: ${{ github.event_name != 'pull_request' && secrets.POSTHOG_PROJECT_TOKEN || '' }} | |
| EXPO_PUBLIC_POSTHOG_HOST: ${{ github.event_name != 'pull_request' && secrets.POSTHOG_HOST || '' }} | |
| run: pnpm -F @linkcode/mobile smoke:export | |
| rust: | |
| name: Rust | |
| runs-on: ${{ vars.CI_RUNNER_LINUX || 'ubuntu-latest' }} | |
| timeout-minutes: 20 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| # rustfmt and clippy come from rust-toolchain.toml's components. | |
| - name: Setup Rust | |
| uses: actions-rust-lang/setup-rust-toolchain@v1 | |
| with: | |
| rustflags: "" | |
| - parallel: | |
| - name: Format | |
| run: cargo fmt --check | |
| - name: Clippy | |
| run: cargo clippy --all-targets --locked -- -D warnings | |
| - name: Test | |
| run: cargo test --locked | |
| config-integration: | |
| name: Cross-repository config contract | |
| if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} | |
| runs-on: ${{ vars.CI_RUNNER_LINUX || 'ubuntu-latest' }} | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Require organization App credentials | |
| env: | |
| BOT_APP_ID: ${{ secrets.BOT_APP_ID }} | |
| BOT_APP_PRIVATE_KEY: ${{ secrets.BOT_APP_PRIVATE_KEY }} | |
| run: | | |
| if [ -z "$BOT_APP_ID" ] || [ -z "$BOT_APP_PRIVATE_KEY" ]; then | |
| echo "::error::BOT_APP_ID and BOT_APP_PRIVATE_KEY must be available so CI can read arcboxlabs/linkcodehq and arcboxlabs/linkcode-config" | |
| exit 1 | |
| fi | |
| - name: Mint publisher read token | |
| id: publisher-token | |
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 | |
| with: | |
| app-id: ${{ secrets.BOT_APP_ID }} | |
| private-key: ${{ secrets.BOT_APP_PRIVATE_KEY }} | |
| owner: arcboxlabs | |
| repositories: linkcodehq | |
| permission-contents: read | |
| - name: Mint config source read token | |
| id: source-token | |
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 | |
| with: | |
| app-id: ${{ secrets.BOT_APP_ID }} | |
| private-key: ${{ secrets.BOT_APP_PRIVATE_KEY }} | |
| owner: arcboxlabs | |
| repositories: linkcode-config | |
| permission-contents: read | |
| - name: Check out pinned publisher | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| repository: arcboxlabs/linkcodehq | |
| ref: 986d9f21403df53bc932f511eb1b5f0bb634d48d | |
| token: ${{ steps.publisher-token.outputs.token }} | |
| path: .config-validation/linkcodehq | |
| persist-credentials: false | |
| - name: Check out pinned config source | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| repository: arcboxlabs/linkcode-config | |
| ref: a1ed4d666721c3aed0d563aaea42fce8b5f945b5 | |
| token: ${{ steps.source-token.outputs.token }} | |
| path: .config-validation/linkcode-config | |
| persist-credentials: false | |
| - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6 | |
| with: | |
| version: 11.9.0 | |
| run_install: false | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: "24" | |
| package-manager-cache: false | |
| - name: Validate pinned source with the pinned parser | |
| run: | | |
| set -euo pipefail | |
| hq="$GITHUB_WORKSPACE/.config-validation/linkcodehq" | |
| config="$GITHUB_WORKSPACE/.config-validation/linkcode-config" | |
| test "$(git -C "$hq" rev-parse HEAD)" = 986d9f21403df53bc932f511eb1b5f0bb634d48d | |
| test "$(git -C "$config" rev-parse HEAD)" = a1ed4d666721c3aed0d563aaea42fce8b5f945b5 | |
| test "$(node --version | cut -d. -f1)" = v24 | |
| test "$(pnpm --version)" = 11.9.0 | |
| pnpm --dir "$hq" --filter @linkcodehq/config-structural... \ | |
| install --frozen-lockfile --ignore-scripts | |
| pnpm --dir "$hq" --filter @linkcodehq/config-structural exec tsx \ | |
| "$config/scripts/validate.mts" \ | |
| --hq-root "$hq" \ | |
| --source-root examples/acme-zenith | |
| all-green: | |
| name: All Green | |
| runs-on: ${{ vars.CI_RUNNER_LINUX || 'ubuntu-latest' }} | |
| timeout-minutes: 5 | |
| needs: | |
| - typescript | |
| - desktop | |
| - webview | |
| - mobile | |
| - rust | |
| - config-integration | |
| if: always() | |
| steps: | |
| - name: Check required jobs | |
| run: | | |
| if [ '${{ needs.typescript.result }}' != 'success' ] || [ '${{ needs.desktop.result }}' != 'success' ] || [ '${{ needs.webview.result }}' != 'success' ] || [ '${{ needs.mobile.result }}' != 'success' ] || [ '${{ needs.rust.result }}' != 'success' ]; then | |
| exit 1 | |
| fi | |
| if [ '${{ needs.config-integration.result }}' != 'success' ] && [ '${{ needs.config-integration.result }}' != 'skipped' ]; then | |
| exit 1 | |
| fi |