Skip to content

Commit 463835d

Browse files
authored
feat(workflow): restore repo variable gate with dedicated poller app (#7780)
## Summary Bring back the `CI_POLLER_HAS_PENDING` variable gate so the cron is truly zero-cost when idle — no runner provisioned at all. Uses a dedicated GitHub App (`CI_POLLER_APP_CLIENT_ID`) for variable writes since neither `GITHUB_TOKEN` nor sentry-internal-app has the `actions_variables:write` permission. The app secret is in the `production` environment for branch protection. ## How it works | Workflow | Trigger | What it does | |---|---|---| | `ci-pending.yml` | `issues: opened` | Adds `ci-pending` label (sentry-internal-app) + sets variable to `true` (poller app) | | `ci-poller.yml` | `schedule: */5` | `if: vars.CI_POLLER_HAS_PENDING == 'true'` — skips when false (no runner). Checks CI, flips labels, sets variable to `false` when done. | ## Cost - **Idle:** zero — job `if` is false, no runner provisioned - **Active:** ~30s per cron tick while there are pending releases - **Previous (without variable):** ~10-15s per tick 24/7 even when idle ## Pre-requisites (already done) - Repo variable: `CI_POLLER_HAS_PENDING` = `false` - Repo variable: `CI_POLLER_APP_CLIENT_ID` - Production environment secret: `CI_POLLER_APP_PRIVATE_KEY`
1 parent 01193fa commit 463835d

2 files changed

Lines changed: 74 additions & 19 deletions

File tree

.github/workflows/ci-pending.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ permissions:
1111
jobs:
1212
mark-pending:
1313
runs-on: ubuntu-latest
14+
environment: production
1415
if: "startsWith(github.event.issue.title, 'publish: ')"
1516
steps:
1617
- name: Get auth token
@@ -20,6 +21,9 @@ jobs:
2021
client-id: ${{ vars.SENTRY_INTERNAL_APP_ID }}
2122
private-key: ${{ secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY }}
2223

24+
# Add the label first — this is the critical step. The poller
25+
# token and variable set below are best-effort optimizations
26+
# that must not block label addition.
2327
- name: Add ci-pending label
2428
env:
2529
# Use the app token so the label event triggers publish.yml
@@ -29,3 +33,21 @@ jobs:
2933
gh issue edit "${{ github.event.issue.number }}" \
3034
-R "$GITHUB_REPOSITORY" \
3135
--add-label "ci-pending"
36+
37+
# Best-effort: enable the cron poller variable. If this fails, the
38+
# poller's cron will still pick up the issue (it just won't skip the
39+
# runner provisioning on idle ticks until the variable is set).
40+
- name: Get poller app token
41+
id: poller-token
42+
continue-on-error: true
43+
uses: actions/create-github-app-token@v3
44+
with:
45+
client-id: ${{ vars.CI_POLLER_APP_CLIENT_ID }}
46+
private-key: ${{ secrets.CI_POLLER_APP_PRIVATE_KEY }}
47+
48+
- name: Enable cron poller
49+
if: steps.poller-token.outcome == 'success'
50+
env:
51+
GH_TOKEN: ${{ steps.poller-token.outputs.token }}
52+
run: |
53+
gh variable set CI_POLLER_HAS_PENDING -R "$GITHUB_REPOSITORY" -b "true"

.github/workflows/ci-poller.yml

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ name: CI Status Poller
33
on:
44
schedule:
55
- cron: "*/5 * * * *"
6+
workflow_dispatch:
67

78
permissions:
89
contents: read
@@ -11,37 +12,23 @@ permissions:
1112
jobs:
1213
check-ci:
1314
runs-on: ubuntu-latest
15+
environment: production
16+
# Skip entirely (no runner provisioned) when there's nothing to check.
17+
# Set to "true" by ci-pending.yml, reset to "false" here when done.
18+
# Always allow workflow_dispatch for manual recovery.
19+
if: vars.CI_POLLER_HAS_PENDING == 'true' || github.event_name == 'workflow_dispatch'
1420
concurrency:
1521
group: ci-status-poller
1622
cancel-in-progress: false
1723
steps:
18-
# Cheap first check with GITHUB_TOKEN — if no ci-pending issues exist,
19-
# exit immediately without generating an app token (~5s idle cost).
20-
- name: Check for pending issues
21-
id: pending
22-
env:
23-
GH_TOKEN: ${{ github.token }}
24-
run: |
25-
count=$(gh issue list -R "$GITHUB_REPOSITORY" \
26-
--state open \
27-
--label ci-pending \
28-
--limit 1 \
29-
--json number -q 'length')
30-
echo "count=${count}" >> "$GITHUB_OUTPUT"
31-
if [[ "$count" == "0" ]]; then
32-
echo "No ci-pending issues. Nothing to do."
33-
fi
34-
3524
- name: Get auth token
36-
if: steps.pending.outputs.count != '0'
3725
id: token
3826
uses: actions/create-github-app-token@v3
3927
with:
4028
client-id: ${{ vars.SENTRY_INTERNAL_APP_ID }}
4129
private-key: ${{ secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY }}
4230

4331
- name: Check CI status for ci-pending issues
44-
if: steps.pending.outputs.count != '0'
4532
env:
4633
# Use the app token so label changes trigger publish.yml
4734
# (GITHUB_TOKEN events are suppressed by GitHub).
@@ -55,6 +42,10 @@ jobs:
5542
--json number,title,labels,body)
5643
5744
count=$(echo "$issues" | jq length)
45+
if [[ "$count" == "0" ]]; then
46+
echo "No ci-pending issues found."
47+
exit 0
48+
fi
5849
echo "Found ${count} ci-pending issue(s)."
5950
6051
# Check each issue's CI status
@@ -182,3 +173,45 @@ jobs:
182173
gh issue comment "$number" -R "$GITHUB_REPOSITORY" --body "$comment"
183174
fi
184175
done
176+
177+
# Cleanup: check if we should disable the poller. Runs even if the
178+
# CI check step above failed, so CI_POLLER_HAS_PENDING doesn't get
179+
# stuck on "true" permanently.
180+
- name: Check for remaining pending issues
181+
if: always()
182+
id: remaining
183+
env:
184+
GH_TOKEN: ${{ github.token }}
185+
run: |
186+
count=$(gh issue list -R "$GITHUB_REPOSITORY" \
187+
--state open \
188+
--label ci-pending \
189+
--limit 1 \
190+
--json number -q 'length')
191+
echo "count=${count}" >> "$GITHUB_OUTPUT"
192+
193+
# Update the poller variable to match reality:
194+
# - Remaining issues → ensure variable is "true" (important when
195+
# workflow_dispatch bypassed the gate while variable was "false")
196+
# - No remaining issues → set to "false" to stop the cron
197+
# Placed after CI check so a token failure can't block CI checking.
198+
- name: Get poller app token
199+
if: always()
200+
id: poller-token
201+
uses: actions/create-github-app-token@v3
202+
with:
203+
client-id: ${{ vars.CI_POLLER_APP_CLIENT_ID }}
204+
private-key: ${{ secrets.CI_POLLER_APP_PRIVATE_KEY }}
205+
206+
- name: Sync poller variable with pending issue state
207+
if: always() && steps.poller-token.outcome == 'success'
208+
env:
209+
GH_TOKEN: ${{ steps.poller-token.outputs.token }}
210+
run: |
211+
if [[ "${{ steps.remaining.outputs.count }}" == "0" ]]; then
212+
echo "All ci-pending issues resolved. Disabling poller."
213+
gh variable set CI_POLLER_HAS_PENDING -R "$GITHUB_REPOSITORY" -b "false"
214+
else
215+
echo "Still pending issues. Ensuring poller stays enabled."
216+
gh variable set CI_POLLER_HAS_PENDING -R "$GITHUB_REPOSITORY" -b "true"
217+
fi

0 commit comments

Comments
 (0)