-
Notifications
You must be signed in to change notification settings - Fork 5
256 lines (225 loc) · 11.4 KB
/
Copy pathmoxygen-sync.yml
File metadata and controls
256 lines (225 loc) · 11.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
name: moxygen sync
# Updates deps/moxygen submodule to latest moxygen main, creates a PR.
# Schedule-driven (daily cron) plus workflow_dispatch for ad-hoc syncs.
# repository_dispatch retained for non-sync flows (e.g., manual hand-merged
# moxygen PRs that need immediate propagation); the routine cron-cascade
# does not use it.
# One PR at a time — blocks if one is pending.
#
# Branch naming: sync-moxygen/<short-sha>
# Auto-merged by auto-merge-moxygen.yml after ci-pr passes.
on:
schedule:
# Fourth (final) stage of the openmoq sync cron cascade (UTC; ET shifts under DST):
# 03:23 picoquic upstream-sync (private-octopus → openmoq/picoquic)
# 04:23 moxygen upstream-sync (facebookexperimental → openmoq/moxygen)
# 04:37 moxygen picoquic-pin sync (openmoq/picoquic → moxygen picoquic-rev.txt)
# 08:23 moqx moxygen-submodule sync (this workflow — openmoq/moxygen → moqx deps/moxygen)
# The 3h46m gap before this stage gives moxygen sync PRs (upstream + picoquic-pin)
# time to run CI and auto-merge into openmoq/moxygen main before we pull it.
- cron: '23 8 * * *'
repository_dispatch:
types: [moxygen-update]
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
sync:
runs-on: ubuntu-22.04
steps:
# ── Generate GitHub App token (bot identity for PRs) ──
- name: Generate app token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.OMOQ_APP_ID }}
private-key: ${{ secrets.OMOQ_APP_PRIV_KEY }}
- uses: actions/checkout@v4
with:
ref: main
submodules: true
token: ${{ steps.app-token.outputs.token }}
# Full history required so we can check out arbitrary moxygen
# SHAs inside the submodule. Shallow (default depth=1) creates
# shallow submodule clones, which fail with "unable to read
# tree" when checking out a SHA whose tree isn't present.
fetch-depth: 0
# ══════════════════════════════════════════════════════════
# PHASE A: Check for blocking sync PR
# ══════════════════════════════════════════════════════════
- name: Check for open sync-moxygen PR
id: blocking
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
PR=$(gh api "repos/${{ github.repository }}/pulls?state=open&base=main" \
--jq '[.[] | select(.head.ref | startswith("sync-moxygen/"))] | .[0] // empty')
if [ -n "$PR" ]; then
PR_NUM=$(echo "$PR" | jq -r '.number')
PR_REF=$(echo "$PR" | jq -r '.head.ref')
echo "::warning::Sync blocked — open PR #$PR_NUM ($PR_REF) pending."
echo "blocked=true" >> "$GITHUB_OUTPUT"
echo "pr_num=$PR_NUM" >> "$GITHUB_OUTPUT"
echo "pr_ref=$PR_REF" >> "$GITHUB_OUTPUT"
else
echo "No open sync-moxygen PR. Proceeding."
echo "blocked=false" >> "$GITHUB_OUTPUT"
fi
- name: Notify blocked (Slack)
if: steps.blocking.outputs.blocked == 'true'
continue-on-error: true
env:
SLACK_WEBHOOK_URL: ${{ secrets.OMOQ_SLACK_WEBHOOK_URL }}
run: |
PR_NUM="${{ steps.blocking.outputs.pr_num }}"
PR_URL="${{ github.server_url }}/${{ github.repository }}/pull/${PR_NUM}"
TEXT=":no_entry: *${{ github.repository }}* moxygen sync blocked: PR <${PR_URL}|#${PR_NUM}> needs resolution"
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
--data "{\"text\": \"${TEXT}\"}"
- name: Notify blocked (email)
if: steps.blocking.outputs.blocked == 'true'
continue-on-error: true
env:
AWS_ACCESS_KEY_ID: ${{ secrets.OMOQ_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.OMOQ_AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: us-east-1
run: |
PR_NUM="${{ steps.blocking.outputs.pr_num }}"
PR_REF="${{ steps.blocking.outputs.pr_ref }}"
PR_URL="${{ github.server_url }}/${{ github.repository }}/pull/${PR_NUM}"
RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
SUBJECT="[moqx] moxygen sync paused — PR #${PR_NUM} pending"
BODY="Moxygen submodule sync blocked by open PR.\n\nPR: ${PR_URL}\nBranch: ${PR_REF}\nRun: ${RUN_URL}\n\nResolve or merge the existing PR, then re-run manually."
aws ses send-email \
--from "noreply@ci.openmoq.org" \
--destination '{"ToAddresses":["github-notifications@openmoq.org"]}' \
--message "{
\"Subject\": {\"Data\": \"${SUBJECT}\"},
\"Body\": {\"Text\": {\"Data\": \"${BODY}\"}}
}"
# ══════════════════════════════════════════════════════════
# PHASE B: Update submodule and create PR
# ══════════════════════════════════════════════════════════
- name: Determine target SHA
if: steps.blocking.outputs.blocked == 'false'
id: target
run: |
CURRENT_SHA=$(git -C deps/moxygen rev-parse HEAD)
echo "current_sha=${CURRENT_SHA}" >> "$GITHUB_OUTPUT"
if [ "${{ github.event_name }}" = "repository_dispatch" ]; then
TARGET_SHA="${{ github.event.client_payload.sha }}"
else
# Manual trigger: fetch latest moxygen main
git -C deps/moxygen fetch origin main
TARGET_SHA=$(git -C deps/moxygen rev-parse origin/main)
fi
echo "target_sha=${TARGET_SHA}" >> "$GITHUB_OUTPUT"
echo "short_sha=${TARGET_SHA:0:7}" >> "$GITHUB_OUTPUT"
if [ "$CURRENT_SHA" = "$TARGET_SHA" ]; then
echo "Submodule already at ${TARGET_SHA:0:7}. Nothing to do."
echo "needs_update=false" >> "$GITHUB_OUTPUT"
else
echo "Will update submodule: ${CURRENT_SHA:0:7} → ${TARGET_SHA:0:7}"
echo "needs_update=true" >> "$GITHUB_OUTPUT"
fi
- name: Create sync branch and PR
if: steps.blocking.outputs.blocked == 'false' && steps.target.outputs.needs_update == 'true'
id: new_pr
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
TARGET_SHA="${{ steps.target.outputs.target_sha }}"
SHORT_SHA="${{ steps.target.outputs.short_sha }}"
BRANCH="sync-moxygen/${SHORT_SHA}"
REPO="${{ github.repository }}"
# Reclaim orphan branch if it exists with no open PR. PHASE A's
# blocking check rejects when an open sync-moxygen/* PR is pending,
# so by this point any pre-existing branch with no open PR is from a
# prior closed-without-merge sync attempt — safe to delete.
if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then
EXISTING_PR=$(gh pr list --repo "$REPO" --state open --head "$BRANCH" \
--json number --jq '.[0].number // empty' 2>/dev/null || true)
if [ -n "$EXISTING_PR" ]; then
echo "::error::Branch $BRANCH already used by open PR #$EXISTING_PR — aborting"
exit 1
fi
echo "::warning::Deleting orphan $BRANCH (closed PR or stale push, no open PR)"
gh api -X DELETE "repos/$REPO/git/refs/heads/$BRANCH"
fi
git config user.email "omoq-sync-bot[bot]@users.noreply.github.com"
git config user.name "omoq-sync-bot[bot]"
git checkout -b "$BRANCH"
# Update submodule to target SHA
git -C deps/moxygen fetch origin
git -C deps/moxygen checkout "$TARGET_SHA"
git add deps/moxygen
git commit -m "sync: update moxygen submodule to ${SHORT_SHA}"
git push -u origin "$BRANCH"
echo "Created branch $BRANCH"
# Determine trigger source for PR body
if [ "${{ github.event_name }}" = "repository_dispatch" ]; then
TRIGGER="Triggered by moxygen \`ci main\` run #${{ github.event.client_payload.run_id }}."
else
TRIGGER="Triggered manually."
fi
PR_NUM=$(gh api repos/${{ github.repository }}/pulls \
-f title="sync: moxygen ${SHORT_SHA}" \
-f body="$(cat <<EOF
Automated moxygen submodule update.
- **Moxygen commit:** [\`${SHORT_SHA}\`](https://github.com/openmoq/moxygen/commit/${TARGET_SHA})
- **Previous:** \`${{ steps.target.outputs.current_sha }}\`
${TRIGGER}
Verification will run automatically. On success, this PR auto-merges.
EOF
)" \
-f head="$BRANCH" \
-f base="main" \
--jq '.number')
echo "Created sync PR #$PR_NUM"
echo "pr_num=$PR_NUM" >> "$GITHUB_OUTPUT"
# ── Approve PR (different identity for required review) ──
- name: Approve PR
if: steps.blocking.outputs.blocked == 'false' && steps.target.outputs.needs_update == 'true'
env:
GH_TOKEN: ${{ secrets.OMOQ_SYNC_TOKEN }}
run: |
PR_NUM="${{ steps.new_pr.outputs.pr_num }}"
[ -z "$PR_NUM" ] && { echo "::warning::No PR number"; exit 0; }
echo "Approving PR #$PR_NUM..."
gh api repos/${{ github.repository }}/pulls/$PR_NUM/reviews \
-f event=APPROVE \
-f body="Auto-approved by moxygen sync workflow." \
--jq '.state'
# ── Failure notifications ──
- name: Notify Slack (sync failure)
if: failure()
continue-on-error: true
env:
SLACK_WEBHOOK_URL: ${{ secrets.OMOQ_SLACK_WEBHOOK_URL }}
run: |
RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
TEXT=":warning: *${{ github.repository }}* moxygen sync failed: <${RUN_URL}|run #${{ github.run_number }}>"
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
--data "{\"text\": \"${TEXT}\"}"
- name: Notify email (sync failure)
if: failure()
continue-on-error: true
env:
AWS_ACCESS_KEY_ID: ${{ secrets.OMOQ_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.OMOQ_AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: us-east-1
run: |
TARGET="${{ steps.target.outputs.target_sha || 'unknown' }}"
RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
SUBJECT="[moqx] moxygen sync FAILED"
BODY="Moxygen submodule sync workflow failed.\n\nTarget SHA: ${TARGET}\nRun: ${RUN_URL}"
aws ses send-email \
--from "noreply@ci.openmoq.org" \
--destination '{"ToAddresses":["github-notifications@openmoq.org"]}' \
--message "{
\"Subject\": {\"Data\": \"${SUBJECT}\"},
\"Body\": {\"Text\": {\"Data\": \"${BODY}\"}}
}"