Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,17 @@ on:
permissions:
contents: read

# Linux jobs run on the fleet's local, ephemeral runners (label pm-local) when the repository variable
# PM_LOCAL_RUNNER is 'on'; fork pull requests always stay on GitHub-hosted runners. Unset or 'off': ubuntu-latest.
# A new push to a pull request supersedes its earlier CI run: cancel it instead of letting both hold runners.
# Pushes to main and manual runs get a group of their own, so they never wait for or cancel each other.
concurrency:
group: ci-${{ github.event.pull_request.number && format('pr-{0}', github.event.pull_request.number) || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
Comment on lines +17 to +18

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Older PR run cancels latest

When two pushes to the same PR are processed out of order and the older push's run enters the concurrency group last, gitHub does not guarantee that runs enter a concurrency group in push order, so an older push's run can enter after a newer push's run and cancel it. The older SHA then gets the remaining CI result while the PR's current commit has no successful required checks.

Before allowing an event's run to cancel another, verify that its head SHA is still the PR's current head, or otherwise serialize runs in commit order.

Prompt for AI agents
In `.github/workflows/ci.yml` at lines 17-18:

**Older PR run cancels latest**

When two pushes to the same PR are processed out of order and the older push's run enters the concurrency group last, gitHub does not guarantee that runs enter a concurrency group in push order, so an older push's run can enter after a newer push's run and cancel it. The older SHA then gets the remaining CI result while the PR's current commit has no successful required checks.

Before allowing an event's run to cancel another, verify that its head SHA is still the PR's current head, or otherwise serialize runs in commit order.


jobs:
test:
runs-on: ubuntu-latest
runs-on: ${{ (vars.PM_LOCAL_RUNNER == 'on' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)) && fromJSON('["self-hosted","pm-local"]') || 'ubuntu-latest' }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High · Fork code reaches local runners

When PM_LOCAL_RUNNER is on and an external contributor's fork PR has its workflow run approved, a fork author can edit this PR-triggered workflow to remove the fork check or add a job targeting self-hosted; the edited workflow then runs PR-controlled code on a local runner, exposing its job token and runner-reachable resources.

Keep PR-controlled workflows off local runners, or enforce local-runner eligibility through a policy that PR authors cannot change.

Also at .github/workflows/ci.yml:82.

Prompt for AI agents
In `.github/workflows/ci.yml` at line 22:

**Fork code reaches local runners**

When PM_LOCAL_RUNNER is `on` and an external contributor's fork PR has its workflow run approved, a fork author can edit this PR-triggered workflow to remove the fork check or add a job targeting `self-hosted`; the edited workflow then runs PR-controlled code on a local runner, exposing its job token and runner-reachable resources.

Keep PR-controlled workflows off local runners, or enforce local-runner eligibility through a policy that PR authors cannot change.

Also at `.github/workflows/ci.yml:82`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Queued checks never fall back

When a local runner becomes unavailable after jobs are queued and the controller turns PM_LOCAL_RUNNER off, runs-on has already resolved to [self-hosted, pm-local], so changing the variable does not reroute those jobs to ubuntu-latest; they stay queued until a matching runner returns or the runs are canceled, leaving the check blocked.

When disabling the local runner, cancel and recreate queued jobs with runs-on: ubuntu-latest rather than relying on the resolved expression to change.

Also at .github/workflows/ci.yml:82.

Prompt for AI agents
In `.github/workflows/ci.yml` at line 22:

**Queued checks never fall back**

When a local runner becomes unavailable after jobs are queued and the controller turns `PM_LOCAL_RUNNER` off, `runs-on` has already resolved to `[self-hosted, pm-local]`, so changing the variable does not reroute those jobs to `ubuntu-latest`; they stay queued until a matching runner returns or the runs are canceled, leaving the check blocked.

When disabling the local runner, cancel and recreate queued jobs with `runs-on: ubuntu-latest` rather than relying on the resolved expression to change.

Also at `.github/workflows/ci.yml:82`.

strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -71,7 +79,7 @@ jobs:
run: npm run test:bun

bun-release:
runs-on: ubuntu-latest
runs-on: ${{ (vars.PM_LOCAL_RUNNER == 'on' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)) && fromJSON('["self-hosted","pm-local"]') || 'ubuntu-latest' }}
strategy:
fail-fast: false
matrix:
Expand Down
Loading