Skip to content

Commit 953ad6f

Browse files
committed
feat(workflow): split CI poller into ci-pending.yml and ci-poller.yml
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.
1 parent a81365e commit 953ad6f

File tree

3 files changed

+165
-104
lines changed

3 files changed

+165
-104
lines changed

.github/workflows/ci-pending.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Mark new publish issues as CI-pending
2+
3+
on:
4+
issues:
5+
types: [opened]
6+
7+
permissions:
8+
contents: read
9+
issues: read
10+
11+
jobs:
12+
mark-pending:
13+
runs-on: ubuntu-latest
14+
if: "startsWith(github.event.issue.title, 'publish: ')"
15+
steps:
16+
- name: Get auth token
17+
id: token
18+
uses: actions/create-github-app-token@v2.2.1
19+
with:
20+
app-id: ${{ vars.SENTRY_INTERNAL_APP_ID }}
21+
private-key: ${{ secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY }}
22+
23+
- name: Add ci-pending label and enable poller
24+
env:
25+
GH_TOKEN: ${{ steps.token.outputs.token }}
26+
run: |
27+
gh issue edit "${{ github.event.issue.number }}" \
28+
-R "$GITHUB_REPOSITORY" \
29+
--add-label "ci-pending"
30+
31+
# Enable the cron poller (ci-poller.yml) so it starts checking
32+
gh variable set CI_POLLER_HAS_PENDING -R "$GITHUB_REPOSITORY" -b "true"

.github/workflows/ci-poller.yml

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
name: CI Status Poller
2+
3+
on:
4+
schedule:
5+
- cron: "*/5 * * * *"
6+
7+
permissions:
8+
contents: read
9+
issues: read
10+
11+
jobs:
12+
check-ci:
13+
runs-on: ubuntu-latest
14+
# Skip entirely (no runner provisioned) when there's nothing to check.
15+
# The CI_POLLER_HAS_PENDING variable is set to "true" by ci-pending.yml
16+
# and reset to "false" here when all pending issues are resolved.
17+
if: vars.CI_POLLER_HAS_PENDING == 'true'
18+
concurrency:
19+
group: ci-status-poller
20+
cancel-in-progress: false
21+
steps:
22+
- name: Get auth token
23+
id: token
24+
uses: actions/create-github-app-token@v2.2.1
25+
with:
26+
app-id: ${{ vars.SENTRY_INTERNAL_APP_ID }}
27+
private-key: ${{ secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY }}
28+
29+
- name: Check CI status for ci-pending issues
30+
env:
31+
# Use the app token so label changes trigger publish.yml
32+
# (GITHUB_TOKEN events are suppressed by GitHub).
33+
GH_TOKEN: ${{ steps.token.outputs.token }}
34+
run: |
35+
# Find all open issues with ci-pending label (include body to extract commit SHA)
36+
issues=$(gh issue list -R "$GITHUB_REPOSITORY" \
37+
--state open \
38+
--label ci-pending \
39+
--limit 200 \
40+
--json number,title,labels,body)
41+
42+
count=$(echo "$issues" | jq length)
43+
if [[ "$count" == "0" ]]; then
44+
echo "No ci-pending issues found. Disabling poller."
45+
gh variable set CI_POLLER_HAS_PENDING -R "$GITHUB_REPOSITORY" -b "false"
46+
exit 0
47+
fi
48+
echo "Found ${count} ci-pending issue(s)."
49+
50+
# Check each issue's CI status
51+
echo "$issues" | jq -c '.[]' | while read -r issue; do
52+
number=$(echo "$issue" | jq -r '.number')
53+
title=$(echo "$issue" | jq -r '.title')
54+
body=$(echo "$issue" | jq -r '.body')
55+
has_accepted=$(echo "$issue" | jq '[.labels[].name] | any(. == "accepted")')
56+
57+
# Parse repo and version from title: "publish: owner/repo@version"
58+
repo=$(echo "$title" | sed -n 's/^publish: \(.*\)@.*/\1/p')
59+
version=$(echo "$title" | sed -n 's/^publish: .*@\(.*\)/\1/p')
60+
61+
if [[ -z "$repo" || -z "$version" ]]; then
62+
echo "::warning::Could not parse repo/version from issue #${number}: ${title}"
63+
continue
64+
fi
65+
66+
# Extract the commit SHA from the "View check runs" link in the issue body.
67+
# The link format is: https://github.com/{owner}/{repo}/commit/{SHA}/checks/
68+
# This avoids hard-coding the release branch name, which repos can customize.
69+
sha=$(echo "$body" | grep -oP '(?<=commit/)[0-9a-f]{40}(?=/checks)' || true)
70+
71+
if [[ -z "$sha" ]]; then
72+
echo "::warning::Could not extract commit SHA from issue #${number} body, skipping."
73+
continue
74+
fi
75+
76+
echo "Checking CI for ${repo}@${version} commit ${sha:0:8} (issue #${number})..."
77+
78+
# Check combined commit status
79+
commit_status=$(gh api "repos/${repo}/commits/${sha}/status" \
80+
--jq '.state' 2>/dev/null || echo "error")
81+
82+
if [[ "$commit_status" == "error" ]]; then
83+
echo " Could not fetch commit status for ${repo}@${sha:0:8}, skipping."
84+
continue
85+
fi
86+
87+
# Check that all check runs are completed
88+
pending_checks=$(gh api "repos/${repo}/commits/${sha}/check-runs" \
89+
--jq '[.check_runs[] | select(.status != "completed")] | length' 2>/dev/null || echo "-1")
90+
91+
failed_checks=$(gh api "repos/${repo}/commits/${sha}/check-runs" \
92+
--jq '[.check_runs[] | select(.conclusion == "failure")] | length' 2>/dev/null || echo "-1")
93+
94+
echo " commit_status=${commit_status} pending_checks=${pending_checks} failed_checks=${failed_checks}"
95+
96+
# CI is ready when: commit status is success (or no statuses reported)
97+
# AND no check runs are still pending AND none have failed
98+
if [[ ("$commit_status" == "success" || "$commit_status" == "pending") \
99+
&& "$pending_checks" == "0" && "$failed_checks" == "0" ]]; then
100+
101+
# Double-check there's at least one completed check run (avoid false positive
102+
# when CI hasn't started yet — all counts would be 0)
103+
total_checks=$(gh api "repos/${repo}/commits/${sha}/check-runs" \
104+
--jq '.total_count' 2>/dev/null || echo "0")
105+
if [[ "$total_checks" == "0" && "$commit_status" != "success" ]]; then
106+
echo " No check runs found and no success status — CI may not have started yet."
107+
continue
108+
fi
109+
110+
echo " CI passed! Adding ci-ready label."
111+
gh issue edit "$number" -R "$GITHUB_REPOSITORY" \
112+
--remove-label "ci-pending" \
113+
--add-label "ci-ready"
114+
115+
if [[ "$has_accepted" == "true" ]]; then
116+
comment="CI checks passed for ${repo}@${version}. Publishing is starting now."
117+
else
118+
comment="CI checks passed for ${repo}@${version}. Publishing will start once the **accepted** label is also present."
119+
fi
120+
gh issue comment "$number" -R "$GITHUB_REPOSITORY" --body "$comment"
121+
fi
122+
done
123+
124+
# Re-check if there are still pending issues; disable poller if none remain
125+
remaining=$(gh issue list -R "$GITHUB_REPOSITORY" \
126+
--state open \
127+
--label ci-pending \
128+
--limit 1 \
129+
--json number -q 'length')
130+
if [[ "$remaining" == "0" ]]; then
131+
echo "All ci-pending issues resolved. Disabling poller."
132+
gh variable set CI_POLLER_HAS_PENDING -R "$GITHUB_REPOSITORY" -b "false"
133+
fi

.github/workflows/ci-ready.yml

Lines changed: 0 additions & 104 deletions
This file was deleted.

0 commit comments

Comments
 (0)