Skip to content

Commit 20b6276

Browse files
Mark backport board items done once they land (#32)
Adds a poller that marks project-board items Done when the source PR is actually present on the target branch, instead of trusting the backport PR body. Signed-off-by: Sarthak Aggarwal <sarthagg@amazon.com>
1 parent cc330e7 commit 20b6276

6 files changed

Lines changed: 1097 additions & 4 deletions

File tree

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
name: Backport Mark Done Poll
2+
3+
# Self-healing reconcile of project-board backport status. For every
4+
# "To be backported" item, verify the source PR's commit is actually on the
5+
# target branch and flip verified items to Done. Independent of any merge hook:
6+
# catches items backported by the sweep, manual cherry-picks, or older runs.
7+
8+
on:
9+
schedule:
10+
# Hourly, offset from the daily sweep so a sweep's results get picked up soon.
11+
- cron: "30 * * * *"
12+
workflow_dispatch:
13+
inputs:
14+
repo:
15+
description: "Filter to this repository (empty = all repos in registry)"
16+
required: false
17+
type: string
18+
default: ""
19+
project_number:
20+
description: "Filter to this project number (empty = all)"
21+
required: false
22+
type: string
23+
default: ""
24+
25+
permissions: {}
26+
27+
jobs:
28+
preflight:
29+
name: "Generate matrix"
30+
if: github.repository == 'valkey-io/valkey-ci-agent'
31+
runs-on: ubuntu-latest
32+
timeout-minutes: 5
33+
permissions:
34+
contents: read
35+
outputs:
36+
matrix: ${{ steps.matrix.outputs.matrix }}
37+
has_entries: ${{ steps.matrix.outputs.has_entries }}
38+
steps:
39+
- name: Check out agent repository
40+
uses: actions/checkout@v6
41+
with:
42+
persist-credentials: false
43+
fetch-depth: 1
44+
45+
- name: Set up Python 3.11
46+
uses: actions/setup-python@v6
47+
with:
48+
python-version: "3.11"
49+
cache: "pip"
50+
51+
- name: Install dependencies
52+
run: pip install pyyaml
53+
54+
- name: Generate matrix from registry
55+
id: matrix
56+
shell: bash
57+
env:
58+
REPO_FILTER: ${{ inputs.repo || '' }}
59+
PROJECT_FILTER: ${{ inputs.project_number || '' }}
60+
run: |
61+
set -euo pipefail
62+
args=(--registry repos.yml)
63+
if [[ -n "${REPO_FILTER}" ]]; then
64+
args+=(--repo "${REPO_FILTER}")
65+
fi
66+
if [[ -n "${PROJECT_FILTER}" ]]; then
67+
args+=(--project-number "${PROJECT_FILTER}")
68+
fi
69+
python -m scripts.backport.matrix "${args[@]}" --output-file "$GITHUB_OUTPUT"
70+
71+
reconcile:
72+
name: "mark-done/${{ matrix.repo }}/${{ matrix.branch }}"
73+
needs: preflight
74+
if: needs.preflight.outputs.has_entries == 'true'
75+
runs-on: ubuntu-latest
76+
timeout-minutes: 20
77+
strategy:
78+
fail-fast: false
79+
matrix: ${{ fromJson(needs.preflight.outputs.matrix) }}
80+
concurrency:
81+
group: backport-mark-done-${{ matrix.repo }}-${{ matrix.branch }}
82+
cancel-in-progress: false
83+
permissions:
84+
contents: read
85+
id-token: write
86+
steps:
87+
- name: Check out agent repository
88+
uses: actions/checkout@v6
89+
with:
90+
persist-credentials: false
91+
fetch-depth: 1
92+
93+
- name: Set up Python 3.11
94+
uses: actions/setup-python@v6
95+
with:
96+
python-version: "3.11"
97+
cache: "pip"
98+
99+
- name: Install dependencies
100+
run: pip install -r requirements.txt
101+
102+
- name: Generate GitHub App token
103+
id: generate-token
104+
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1.12.0
105+
with:
106+
app-id: ${{ secrets.VALKEYRIE_BOT_APP_ID }}
107+
private-key: ${{ secrets.VALKEYRIE_BOT_PRIVATE_KEY }}
108+
owner: valkey-io
109+
110+
- name: Reconcile board status against branch
111+
shell: bash
112+
env:
113+
TARGET_TOKEN: ${{ steps.generate-token.outputs.token }}
114+
REPO: ${{ matrix.repo }}
115+
BRANCH: ${{ matrix.branch }}
116+
run: |
117+
set -euo pipefail
118+
python -m scripts.backport.mark_done \
119+
--registry repos.yml \
120+
--repo "${REPO}" \
121+
--target-branch "${BRANCH}" \
122+
--target-token "${TARGET_TOKEN}" \
123+
--verbose | tee mark-done-result.json
124+
125+
- name: Upload result
126+
if: always()
127+
uses: actions/upload-artifact@v4
128+
with:
129+
name: mark-done-result-${{ github.run_id }}-${{ matrix.repo_slug }}-${{ matrix.branch }}
130+
path: mark-done-result.json
131+
retention-days: 30

README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ The currently active workflow. Cherry-picks merged PRs onto release branches wit
3737
4. **AI conflict resolution** — when cherry-pick conflicts, Claude Code reads both sides and resolves the conflict in place
3838
5. **Validation** — registry-configured build commands run before push; any failure blocks the push
3939
6. **PR creation** — pushes the branch and opens (or updates) a PR with a summary table
40+
7. **Status sync** — after a backport PR is merged into the release branch, the source PR's Project v2 status can be moved from "To be backported" to "Done"
4041

4142
Manual single-PR backports are also supported via `workflow_dispatch`.
4243

@@ -79,7 +80,7 @@ See [`examples/repos.yml`](examples/repos.yml) for a multi-module example.
7980
- `contents:write` on each repo in the registry (for pushing branches)
8081
- `pull-requests:write` on each repo (for opening PRs)
8182
- `issues:write` on each repo (for backport status comments)
82-
- `organization_projects:read` on the org (for querying project boards)
83+
- `organization_projects:write` on the org (for querying and updating project boards)
8384
- An AWS account with Bedrock access to `us.anthropic.claude-opus-4-8`
8485
- An OIDC trust between GitHub Actions and your AWS account
8586

@@ -121,6 +122,32 @@ gh workflow run manual-backport.yml \
121122

122123
Creates one PR named `[Backport 9.0] <original title>`.
123124

125+
#### Mark merged backports done
126+
127+
A scheduled poller reconciles each project board against branch reality and
128+
flips items from `To be backported` to `Done` once the backport actually lands.
129+
It runs hourly via `backport-mark-done-poll.yml` and reconciles every repo and
130+
branch in `repos.yml`. Because it reconciles the whole board on every run, it is
131+
self-healing: it picks up backports applied by the sweep, by an earlier run, or
132+
by a manual cherry-pick, without depending on a merge event.
133+
134+
An item is marked `Done` only when the source PR's commit is genuinely on the
135+
target branch — verified by the cherry-pick's trailing `(#N)` subject, or by the
136+
PR appearing in a sweep commit's `## Applied` table. A backport PR body that
137+
merely claims a PR was applied can never mark it `Done` on its own.
138+
139+
Run it manually for a single repo (omit `--target-branch` to reconcile every
140+
configured branch), and add `--dry-run` to report what would change without
141+
mutating the board:
142+
143+
```bash
144+
python -m scripts.backport.mark_done \
145+
--repo valkey-io/valkey \
146+
--target-branch 9.1 \
147+
--target-token "$TOKEN" \
148+
--dry-run
149+
```
150+
124151
#### Filtering the sweep
125152

126153
To run only for a specific repo or branch:

0 commit comments

Comments
 (0)