Skip to content

feat(workflow): replace dispatch-based ci-ready with centralized CI status poller#7674

Open
BYK wants to merge 1 commit intomainfrom
feat/ci-status-poller
Open

feat(workflow): replace dispatch-based ci-ready with centralized CI status poller#7674
BYK wants to merge 1 commit intomainfrom
feat/ci-status-poller

Conversation

@BYK
Copy link
Copy Markdown
Member

@BYK BYK commented Apr 1, 2026

Summary

Replace per-release idle polling inside Docker containers with a cron-based CI status poller gated by a repo variable. Zero changes needed in craft or target repos.

How it works

Two new workflow files:

ci-pending.yml — triggered on issues: opened

When a new publish issue is created, adds the ci-pending label and sets CI_POLLER_HAS_PENDING repo variable to true to activate the cron poller.

ci-poller.yml — cron every 5 minutes

  • Job-level if: vars.CI_POLLER_HAS_PENDING == 'true' — when false, no runner is provisioned. Zero cost.
  • When true: fetches all ci-pending issues, extracts the commit SHA from the "View check runs" link in each issue body, checks CI status via GitHub API
  • When all checks pass: replaces ci-pending with ci-ready label (using GitHub App token so the label event triggers publish.yml)
  • When all pending issues are resolved: sets the variable back to false, disabling the cron until the next release

Existing publish.yml gate (merged in #7664)

  • accepted + no ci-pending → publish (old repos with legacy polling, or new repos where CI passed)
  • accepted + ci-pending → skip (waiting-for-ci job comments)
  • accepted + ci-ready → publish

Why

craft publish currently polls GitHub's commit status API every 30 seconds for up to 60 minutes inside a Docker container. This idle polling is billed as GitHub Actions minutes and steals from our available capacity — a publish job that waits 45 minutes for CI burns 45 minutes of runner time doing nothing. For repos like sentry-native (~1h 23m CI), it also exhausts the 1-hour GitHub App token lifetime, causing expired-token failures (getsentry/craft#788).

With the poller, the publish job doesn't start at all until CI passes — the expensive Docker job gets a fresh token and runs only for the actual publishing.

Cost

  • Idle (no pending releases): zero — the cron job is skipped at the if level, no runner provisioned
  • Active: ~30 seconds per cron tick to check CI status, every 5 minutes, only while there are ci-pending issues
  • Current approach for comparison: 30-60 minutes of continuous in-Docker polling per release

Pre-deploy

Create the CI_POLLER_HAS_PENDING repo variable with initial value false:

gh variable set CI_POLLER_HAS_PENDING -R getsentry/publish -b "false"

Backward Compatibility

  • Issues without ci-pending label (created before this deploys) → publish.yml gate treats them as old-style, runs with legacy in-Docker polling. Zero behavioral change.
  • New issues (created after deploy) → get ci-pending from ci-pending.yml, poller handles them.

@BYK BYK force-pushed the feat/ci-status-poller branch 8 times, most recently from b9db9cd to 953ad6f Compare April 2, 2026 16:19
@BYK BYK marked this pull request as ready for review April 2, 2026 16:22
@BYK BYK requested a review from a team as a code owner April 2, 2026 16:22
# CI is ready when: commit status is success (or no statuses reported)
# AND no check runs are still pending AND none have failed
if [[ ("$commit_status" == "success" || "$commit_status" == "pending") \
&& "$pending_checks" == "0" && "$failed_checks" == "0" ]]; then
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pending commit status conflated with absent statuses

Medium Severity

The condition allows commit_status == "pending" to pass through, but the GitHub combined status API returns "pending" both when no statuses exist (repo uses only check runs) and when an actual status context is still pending. For repos that use both commit statuses and check runs, this could mark CI as ready while legacy status checks are still running.

Fix in Cursor Fix in Web

@BYK BYK force-pushed the feat/ci-status-poller branch from 953ad6f to b25e625 Compare April 2, 2026 16:38
@BYK BYK force-pushed the feat/ci-status-poller branch from b25e625 to ca538ee Compare April 2, 2026 16:47
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 3 total unresolved issues (including 1 from previous review).

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

ci-pending.yml: triggered on issues:opened, adds ci-pending label
  and sets CI_POLLER_HAS_PENDING repo variable to "true".

ci-poller.yml: cron (*/5 min) with job-level if that checks
  vars.CI_POLLER_HAS_PENDING — when "false", no runner is provisioned
  (zero cost). When "true", checks CI status for all ci-pending issues
  and flips to ci-ready when green. Resets variable to "false" when
  all pending issues are resolved.

Requires creating a CI_POLLER_HAS_PENDING repo variable (initial
value: "false") before deploying.
@BYK BYK force-pushed the feat/ci-status-poller branch from ca538ee to 296cb5a Compare April 2, 2026 16:57
Copy link
Copy Markdown
Member

@Jeffreyhung Jeffreyhung left a comment

Choose a reason for hiding this comment

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

save us those 💸 💸 💸

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants