-
Notifications
You must be signed in to change notification settings - Fork 0
183 lines (165 loc) · 7.67 KB
/
Copy pathauto-sync.yml
File metadata and controls
183 lines (165 loc) · 7.67 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
name: auto-sync-upstream
# Automatically syncs the fork with upstream google/adk-python.
#
# Flow (delegated to functions in scripts/lib/sync-core.sh):
# 1. Merge upstream into main. Content conflicts under .github/workflows/**
# auto-resolve via .gitattributes; modify/delete conflicts there
# auto-resolve in sync_merge_upstream. Real conflicts stop the merge.
# 2. Protect the two fork-owned workflow files (belt-and-braces, in case
# .gitattributes itself got rewritten by the same merge).
# 3. Install + run the model tests.
# 4. Tests pass -> publish (advance both main and stable).
# 5. Re-disable inherited upstream workflows that may have re-activated.
# 6. Any failure -> open (or comment on the existing) auto-sync issue.
#
# Consumers pin `@stable`, so they only ever receive green syncs and are
# automatically held at the last working version when a sync fails.
#
# WHY THE SHARED LIBRARY: this workflow AND scripts/update-fork.sh both
# source scripts/lib/sync-core.sh, so manual and automated recoveries
# execute mechanically-identical logic. Missing that parity caused an
# outage on 2026-07-02: the re-disable step lived only in this YAML, so
# a manual recovery via update-fork.sh left Continuous Integration
# active, which then failed on lint against our patch.
#
# Authentication model:
# Checkout + push use SYNC_TOKEN (a fine-grained PAT with `workflow: write`
# scope) instead of the built-in GITHUB_TOKEN. The built-in token cannot
# push changes under `.github/workflows/**` (GitHub gates that behind the
# `workflow` OAuth scope, which only PATs carry).
#
# Security: only static commands and trusted context values (github.run_id,
# github.repository, github.server_url) plus this workflow's own step outputs
# are used. No untrusted github.event.* input is interpolated into run steps.
on:
schedule:
- cron: '0 6 * * *' # daily 06:00 UTC
workflow_dispatch: {}
permissions:
contents: write # governs GITHUB_TOKEN (used by the failure-report step).
issues: write # The actual push uses SYNC_TOKEN, which is independent
actions: write # of these permissions.
concurrency:
group: auto-sync
cancel-in-progress: false
env:
INTEGRATION_BRANCH: main
STABLE_BRANCH: stable
UPSTREAM_REPO: https://github.com/google/adk-python.git
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Checkout integration branch (full history)
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
# PAT, not GITHUB_TOKEN: needed so the later push step can write
# changes under .github/workflows/**.
token: ${{ secrets.SYNC_TOKEN }}
- name: Configure git and merge drivers
run: |
. "$GITHUB_WORKSPACE/scripts/lib/sync-core.sh"
sync_configure_git
- name: Merge upstream
id: merge
run: |
. "$GITHUB_WORKSPACE/scripts/lib/sync-core.sh"
sync_ensure_upstream_remote
sync_merge_upstream
echo "before=$SYNC_BEFORE" >> "$GITHUB_OUTPUT"
echo "after=$SYNC_AFTER" >> "$GITHUB_OUTPUT"
echo "result=$SYNC_RESULT" >> "$GITHUB_OUTPUT"
# Belt-and-braces: if the same merge that changed our workflow files
# ALSO rewrote .gitattributes, merge=ours might not have been in
# effect. Re-restore our version of the two we own from pre-merge.
- name: Protect fork-owned workflows
if: steps.merge.outputs.result == 'merged'
env:
BEFORE: ${{ steps.merge.outputs.before }}
run: |
. "$GITHUB_WORKSPACE/scripts/lib/sync-core.sh"
sync_protect_fork_workflows "$BEFORE"
- name: Set up Python
if: steps.merge.outputs.result == 'merged'
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install
if: steps.merge.outputs.result == 'merged'
run: pip install -e ".[test]"
- name: Run model tests
id: tests
if: steps.merge.outputs.result == 'merged'
run: python -m pytest tests/unittests/models/ -q
# --- success path: merge clean + tests green -> advance both branches ---
- name: Publish new baseline
if: steps.merge.outputs.result == 'merged' && steps.tests.outcome == 'success'
run: |
. "$GITHUB_WORKSPACE/scripts/lib/sync-core.sh"
sync_publish_baseline
# Re-disable any inherited upstream workflows that may have re-activated.
# Runs on BOTH the merged-and-green path AND the up-to-date path so an
# inherited workflow that quietly re-activated between our syncs (e.g.
# because someone pushed to main directly, touching workflow files) still
# gets caught. See sync-core.sh's UNWANTED_INHERITED_WORKFLOWS list.
- name: Re-disable inherited workflows
if: >-
steps.merge.outputs.result == 'up-to-date' ||
(steps.merge.outputs.result == 'merged' && steps.tests.outcome == 'success')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
run: |
. "$GITHUB_WORKSPACE/scripts/lib/sync-core.sh"
sync_redisable_inherited_workflows
# --- nothing to do ---
- name: Up to date
if: steps.merge.outputs.result == 'up-to-date'
run: echo "Already in sync with upstream. Nothing to do."
# --- failure path: any failure (conflict, red tests, push rejection) -> hold + report ---
- name: Report sync failure
if: >-
failure() ||
steps.merge.outputs.result == 'conflict' ||
(steps.merge.outputs.result == 'merged' && steps.tests.outcome != 'success')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Pin every gh call to THIS repo. We added `upstream` as a git remote
# earlier in the job; the gh CLI infers its default repo from git
# remotes and may pick the wrong one (a recent run targeted
# google/adk-python's labels API and got a 403).
REPO: ${{ github.repository }}
REASON: >-
${{ steps.merge.outputs.result == 'conflict' && 'merge conflict' ||
(steps.merge.outputs.result == 'merged' && steps.tests.outcome != 'success') && 'test failure' ||
'publish failed (see run log)' }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
STABLE_SHA=$(git rev-parse --short "origin/$STABLE_BRANCH")
TITLE="Upstream sync failed ($REASON) — holding stable at $STABLE_SHA"
BODY=$(cat <<EOF
Automated upstream sync could not complete.
- **Reason:** $REASON
- **\`$STABLE_BRANCH\` is held at:** \`$STABLE_SHA\` (last known-good — consumers are safe)
- **Run log:** $RUN_URL
### To recover
\`\`\`
./scripts/update-fork.sh
\`\`\`
Resolve the conflict or fix the failing test, push \`$INTEGRATION_BRANCH\`,
then fast-forward \`$STABLE_BRANCH\` to it.
EOF
)
# Ensure the label exists BEFORE any gh call that filters on it.
if ! gh label list --repo "$REPO" --search auto-sync --json name --jq '.[].name' | grep -qx auto-sync; then
gh label create auto-sync --repo "$REPO" --color B60205 --description "Automated upstream sync"
fi
EXISTING=$(gh issue list --repo "$REPO" --state open --label auto-sync --json number --jq '.[0].number')
if [ -n "$EXISTING" ]; then
gh issue comment "$EXISTING" --repo "$REPO" --body "$BODY"
else
gh issue create --repo "$REPO" --title "$TITLE" --body "$BODY" --label auto-sync
fi
exit 1