Skip to content

Poll for new RC tags in the runner-group reconcile - #8510

Open
atalman wants to merge 1 commit into
pytorch:mainfrom
atalman:atalman/runner-groups-schedule
Open

Poll for new RC tags in the runner-group reconcile#8510
atalman wants to merge 1 commit into
pytorch:mainfrom
atalman:atalman/runner-groups-schedule

Conversation

@atalman

@atalman atalman commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

v2.14.0-rc2 was tagged at 12:36 UTC today, and all 7 mt-rel-* jobs of its docker-release run have been queued ever since. The runner-group allow-list still pins v2.14.0-rc1, and GitHub matches selected_workflows on the exact ref, so nothing is authorized to pick them up.

Why the allow-list is stale

#8502 taught the reconcile to allow-list RC tags — that part works. What is missing is anything that re-runs it when a tag is cut.

RC tags are created in pytorch/pytorch, which cannot trigger a workflow in test-infra. The only automatic trigger here was a push touching generate_binary_build_matrix.py. So:

time (UTC) event
Aug 11 19:09 v2.14.0-rc1 tagged
Aug 11 23:15 #8502 merges — no run triggered, its own script is not in the push paths
Aug 11 23:16, 23:17 manual dispatches — this is what actually deployed #8502, pinning rc1
Aug 11 23:34 push run via #8503, still rc1
Aug 12 12:36 v2.14.0-rc2 tagged
Aug 12 12:38 7 jobs queue at refs/tags/v2.14.0-rc2
no run on main since Aug 11 23:34

The allow-list is only ever as fresh as the last unrelated matrix change, or the last time somebody remembered to dispatch by hand.

#8502 deliberately skipped a cron — "the desired state only changes at release milestones, so run on demand". That reasoning is right about frequency and wrong about observability: a new RC is a milestone, but it happens in another repo, so "on demand" silently means "a human notices the queue".

Changes

Hourly schedule, which applies. A polling run that only ever dry-ran would discover the new RC and then do nothing about it, so SHOULD_APPLY now includes schedule. Cron is at :23 to dodge the top-of-hour scheduling backlog. This caps staleness at ~1h instead of unbounded.

release_manage_runner_groups.py and this workflow added to the push paths. They were absent, so a change to the reconcile logic never deployed itself — exactly why #8502 needed a manual dispatch a minute after merging. Second-order, but it is how the first hole stayed invisible.

cancel-in-progress: false. Harmless when runs were rare; with an hourly trigger it would let the scheduled run kill a release-time manual dispatch mid-apply and leave the allow-list half-written. Runs are short API calls, so queueing costs little.

Verified

actionlint (1.6.21, the pinned version) clean. The apply gate resolves as intended for every event:

ref event apply input applies?
main push yes matrix / script change lands
main schedule yes hourly poll picks up a new RC
main workflow_dispatch true yes manual apply
main workflow_dispatch false no manual dry-run (the default)
PR ref pull_request no discovery only, no token

The PR path stays discovery-only: the token is gated behind environment: ${{ github.ref == 'refs/heads/main' && 'runner-group' || '' }}, so forks and PRs never see it.

This does not unblock rc2 by itself

Landing this starts the polling, but the currently-queued jobs need the allow-list updated now. Someone with access should dispatch release-manage-runner-groups on main with apply: true — the allow-list is evaluated when a job starts, so the queued jobs should pick up without a re-run. Worth doing a dry run first to confirm v2.14.0-rc2 appears in the computed set.

I inferred the stale-allow-list diagnosis from the run history plus #8502's newest-only pinning; I cannot read the runner-group config directly, since that needs RUNNER_GROUP_TOKEN. If an apply: true run does not release the queue, the cause is elsewhere and this PR is still worth having on its own merits.

Frequency is a judgement call

Hourly trades ~24 cheap API-only runs/day against a worst case of ~1h of queued release builds. Happy to tighten to */30 or */15 if a release-day hour of dead queue is too much.

v2.14.0-rc2 was tagged at 12:36 UTC today and all 7 mt-rel-* jobs of its
docker-release run have been queued ever since, because the runner-group
allow-list still pins v2.14.0-rc1 and GitHub matches selected_workflows on
the exact ref.

pytorch#8502 taught the reconcile to allow-list RC tags, but nothing re-runs it when
a tag is cut: RC tags are created in pytorch/pytorch, which cannot trigger a
workflow here, and the only automatic trigger was a push touching
generate_binary_build_matrix.py. The last apply was 2026-08-11T23:34Z, before
rc2 existed. So the allow-list is only ever as fresh as the last unrelated
matrix change or the last time somebody remembered to dispatch by hand.

Add an hourly schedule, and let it apply -- a polling run that only ever
dry-ran would discover the new RC and do nothing about it.

Two related holes closed while here:

- release_manage_runner_groups.py and this workflow were absent from the push
  paths, so a change to the reconcile logic never deployed itself. That is why
  pytorch#8502 needed a manual dispatch a minute after it merged.
- concurrency was cancel-in-progress, which was harmless when runs were rare
  but now lets the hourly run kill a release-time manual dispatch mid-apply
  and leave the allow-list half-written. Runs are short API calls, so queue
  them instead.
@vercel

vercel Bot commented Aug 12, 2026

Copy link
Copy Markdown

@atalman is attempting to deploy a commit to the Meta Open Source Team on Vercel.

A member of the Team first needs to authorize it.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 12, 2026
description: "Apply changes (otherwise dry-run)"
type: boolean
default: false
schedule:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I thought that we didn't want a schedule run and would rely only on workflow dispatch? Having it too is ok though as it does no harm IMO

@huydhn huydhn left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!

pytorchmergebot pushed a commit to pytorch/pytorch that referenced this pull request Aug 12, 2026
Release binaries build from **tags**, and the self-hosted runner groups they need are allow-listed per **exact ref** by a reconcile job in [pytorch/test-infra](https://github.com/pytorch/test-infra/actions/workflows/release-manage-runner-groups.yml). That job cannot observe a tag push in this repo, so today the allow-list only refreshes when an unrelated change lands in test-infra, or when somebody dispatches it by hand.

When it is stale, a freshly cut RC is not authorized for the release runner groups and its build jobs **queue indefinitely, with no error**.

That is what happened to `v2.14.0-rc2`. It was tagged at 12:36 UTC; all 7 `mt-rel-*` jobs of its [docker-release run](https://github.com/pytorch/pytorch/actions/runs/31597398139) were still queued ~2h later. The allow-list still pinned `v2.14.0-rc1`, because the last reconcile had run the previous evening — before rc2 existed. A test-infra dry run confirmed the exact gap:

```
Target refs: [... 'refs/tags/v2.14.0-rc2', ...]
Discovered 9 release workflow(s) on pytorch/pytorch@v2.14.0-rc2
    + .../docker-release.yml@refs/tags/v2.14.0-rc2
    - .../docker-release.yml@refs/tags/v2.14.0-rc1
```

## What this does

On a release tag push, dispatch the test-infra reconcile. It is dispatched on test-infra's `main` rather than at this tag — the reconcile discovers the new tag itself via `git/matching-refs`, and the workflow only exists on that default branch.

Design points worth noting:

- **`runs-on: ubuntu-latest`** — deliberately GitHub-hosted, so this is never gated by the very runner groups it exists to unblock.
- **Tag filters match `create_release.yml` and `docker-release.yml`** (`v[0-9]+.[0-9]+.[0-9]+` and the `-rc[0-9]+` form), so it fires for exactly the tags that build release binaries, and not for `ciflow/*` tags.
- **A failed dispatch fails the job**, surfacing in the tag's checks, rather than silently leaving the queue stuck — which is the failure mode this is meant to eliminate.
- **`permissions: contents: read`**; the cross-repo call uses `GH_PYTORCHBOT_TOKEN`.

## Two things reviewers should check

**1. Token scope.** This uses `secrets.GH_PYTORCHBOT_TOKEN`, which is already used cross-repo here — `nightly.yml` and `weekly.yml` pass it to the commit-hash-update action targeting other org repos. If it is a classic PAT with `repo` scope it can already dispatch test-infra workflows and no new secret is needed, but **I could not verify its scopes**. Someone who can should confirm `actions: write` on `pytorch/test-infra` before relying on this. If it turns out to be insufficient, the alternative is a narrowly scoped app token.

**2. This does nothing for 2.14 until cherry-picked.** A tag build runs the workflow as of the tagged commit, so landing here only covers tags cut from `main`. For `v2.14.0-rc3` to be covered it must be on `release/2.14` before rc3 is cut. @atalman is handling that cherry-pick.

## Related

pytorch/test-infra#8510 adds an hourly poll on the test-infra side. The two are complementary rather than redundant: this hook is the fast path (~1-2 min), and the poll is the backstop for tags cut from branches that do not carry this workflow, for manual runner-group edits drifting, and for reconcile-script changes.

## Test plan

`lintrunner` ACTIONLINT clean (pinned actionlint via `.lintbin`). The dispatch path cannot be exercised before merge, since tag-triggered workflows do not run from a PR ref — it will first execute on the next release tag cut from a branch carrying it.
Pull Request resolved: #193186
Approved by: https://github.com/huydhn
atalman added a commit to pytorch/pytorch that referenced this pull request Aug 13, 2026
Refresh release runner groups when a release tag is pushed (#193186)

Release binaries build from **tags**, and the self-hosted runner groups they need are allow-listed per **exact ref** by a reconcile job in [pytorch/test-infra](https://github.com/pytorch/test-infra/actions/workflows/release-manage-runner-groups.yml). That job cannot observe a tag push in this repo, so today the allow-list only refreshes when an unrelated change lands in test-infra, or when somebody dispatches it by hand.

When it is stale, a freshly cut RC is not authorized for the release runner groups and its build jobs **queue indefinitely, with no error**.

That is what happened to `v2.14.0-rc2`. It was tagged at 12:36 UTC; all 7 `mt-rel-*` jobs of its [docker-release run](https://github.com/pytorch/pytorch/actions/runs/31597398139) were still queued ~2h later. The allow-list still pinned `v2.14.0-rc1`, because the last reconcile had run the previous evening — before rc2 existed. A test-infra dry run confirmed the exact gap:

```
Target refs: [... 'refs/tags/v2.14.0-rc2', ...]
Discovered 9 release workflow(s) on pytorch/pytorch@v2.14.0-rc2
    + .../docker-release.yml@refs/tags/v2.14.0-rc2
    - .../docker-release.yml@refs/tags/v2.14.0-rc1
```

## What this does

On a release tag push, dispatch the test-infra reconcile. It is dispatched on test-infra's `main` rather than at this tag — the reconcile discovers the new tag itself via `git/matching-refs`, and the workflow only exists on that default branch.

Design points worth noting:

- **`runs-on: ubuntu-latest`** — deliberately GitHub-hosted, so this is never gated by the very runner groups it exists to unblock.
- **Tag filters match `create_release.yml` and `docker-release.yml`** (`v[0-9]+.[0-9]+.[0-9]+` and the `-rc[0-9]+` form), so it fires for exactly the tags that build release binaries, and not for `ciflow/*` tags.
- **A failed dispatch fails the job**, surfacing in the tag's checks, rather than silently leaving the queue stuck — which is the failure mode this is meant to eliminate.
- **`permissions: contents: read`**; the cross-repo call uses `GH_PYTORCHBOT_TOKEN`.

## Two things reviewers should check

**1. Token scope.** This uses `secrets.GH_PYTORCHBOT_TOKEN`, which is already used cross-repo here — `nightly.yml` and `weekly.yml` pass it to the commit-hash-update action targeting other org repos. If it is a classic PAT with `repo` scope it can already dispatch test-infra workflows and no new secret is needed, but **I could not verify its scopes**. Someone who can should confirm `actions: write` on `pytorch/test-infra` before relying on this. If it turns out to be insufficient, the alternative is a narrowly scoped app token.

**2. This does nothing for 2.14 until cherry-picked.** A tag build runs the workflow as of the tagged commit, so landing here only covers tags cut from `main`. For `v2.14.0-rc3` to be covered it must be on `release/2.14` before rc3 is cut. @atalman is handling that cherry-pick.

## Related

pytorch/test-infra#8510 adds an hourly poll on the test-infra side. The two are complementary rather than redundant: this hook is the fast path (~1-2 min), and the poll is the backstop for tags cut from branches that do not carry this workflow, for manual runner-group edits drifting, and for reconcile-script changes.

## Test plan

`lintrunner` ACTIONLINT clean (pinned actionlint via `.lintbin`). The dispatch path cannot be exercised before merge, since tag-triggered workflows do not run from a PR ref — it will first execute on the next release tag cut from a branch carrying it.
Pull Request resolved: #193186
Approved by: https://github.com/huydhn

(cherry picked from commit 5c9459b)

Co-authored-by: Andrey Talman <atalman@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants