Repository navigation
178 lines (167 loc) · 6.8 KB
/
Copy pathupstream-sync.yml
File metadata and controls
178 lines (167 loc) · 6.8 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
name: Weekly Upstream Sync
on:
schedule:
- cron: '17 9 * * 1'
workflow_dispatch:
permissions:
actions: write
contents: write
pull-requests: write
concurrency:
group: upstream-sync
cancel-in-progress: false
jobs:
sync:
runs-on: ubuntu-24.04
steps:
- name: Checkout fork main
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
ref: main
fetch-depth: 0
- name: Fetch upstream
shell: bash
run: |
set -euo pipefail
git remote add upstream https://github.com/encrypted-spaces/prototype.git
git fetch upstream main
- name: Open or reuse compatibility PR
id: pr
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
if git merge-base --is-ancestor upstream/main HEAD; then
echo "Fork already contains upstream main"
echo "url=" >> "$GITHUB_OUTPUT"
exit 0
fi
upstream_sha="$(git rev-parse upstream/main)"
fork_main_sha="$(git rev-parse main)"
branch="automation/upstream-sync-${upstream_sha:0:12}"
git config user.name github-actions[bot]
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
existing="$(gh pr list \
--repo "$GITHUB_REPOSITORY" \
--state open \
--head "$branch" \
--json url \
--jq '.[0].url // empty')"
if [[ -n "$existing" ]]; then
git fetch origin "$branch"
if git merge-base --is-ancestor main "origin/$branch" && \
git merge-base --is-ancestor upstream/main "origin/$branch"; then
echo "compatible=true" >> "$GITHUB_OUTPUT"
else
echo "compatible=false" >> "$GITHUB_OUTPUT"
fi
candidate_sha="$(git rev-parse "origin/$branch")"
{
echo "url=$existing"
echo "branch=$branch"
echo "candidate_sha=$candidate_sha"
echo "fork_main_sha=$fork_main_sha"
echo "upstream_sha=$upstream_sha"
} >> "$GITHUB_OUTPUT"
exit 0
fi
git switch -c "$branch"
if git merge --no-edit upstream/main; then
compatible=true
else
git merge --abort
git switch -C "$branch" upstream/main
compatible=false
fi
git push origin "HEAD:refs/heads/$branch"
url="$(gh pr create \
--repo "$GITHUB_REPOSITORY" \
--base main \
--head "$branch" \
--title "chore: sync encrypted-spaces upstream" \
--body "Automated weekly compatibility update to upstream commit \`$upstream_sha\`. Normal pull-request checks validate the fork delta and runtime bridge.")"
candidate_sha="$(git rev-parse "origin/$branch")"
{
echo "url=$url"
echo "branch=$branch"
echo "compatible=$compatible"
echo "candidate_sha=$candidate_sha"
echo "fork_main_sha=$fork_main_sha"
echo "upstream_sha=$upstream_sha"
} >> "$GITHUB_OUTPUT"
- name: Run compatibility CI and merge when green
if: steps.pr.outputs.url != '' && steps.pr.outputs.compatible == 'true'
env:
GH_TOKEN: ${{ github.token }}
PR_URL: ${{ steps.pr.outputs.url }}
CANDIDATE_SHA: ${{ steps.pr.outputs.candidate_sha }}
FORK_MAIN_SHA: ${{ steps.pr.outputs.fork_main_sha }}
UPSTREAM_SHA: ${{ steps.pr.outputs.upstream_sha }}
SYNC_BRANCH: ${{ steps.pr.outputs.branch }}
shell: bash
run: |
set -euo pipefail
trigger_and_wait() {
workflow="$1"
candidate_sha="$2"
fork_main_sha="$3"
upstream_sha="$4"
correlation_id="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${candidate_sha:0:12}"
run_title="upstream-compat $candidate_sha $fork_main_sha $upstream_sha $correlation_id"
gh workflow run "$workflow" --ref main \
-f "candidate_sha=$candidate_sha" \
-f "fork_main_sha=$fork_main_sha" \
-f "upstream_sha=$upstream_sha" \
-f "correlation_id=$correlation_id"
run_id=""
for _ in $(seq 1 60); do
run_id="$(gh run list --workflow "$workflow" --branch main \
--event workflow_dispatch --limit 100 \
--json databaseId,displayTitle,headSha | \
jq -r --arg title "$run_title" --arg head "$fork_main_sha" \
'.[] | select(.displayTitle == $title and .headSha == $head) | .databaseId' | \
head -n 1)"
if [[ -n "$run_id" ]]; then
break
fi
sleep 5
done
[[ -n "$run_id" ]] || return 1
timeout 3600 gh run watch "$run_id" --exit-status
gh run view "$run_id" --json displayTitle,headSha,event,conclusion | \
jq -e --arg title "$run_title" --arg head "$fork_main_sha" '
.displayTitle == $title and
.headSha == $head and
.event == "workflow_dispatch" and
.conclusion == "success"
' >/dev/null
}
if ! trigger_and_wait upstream-compat.yml "$CANDIDATE_SHA" "$FORK_MAIN_SHA" "$UPSTREAM_SHA"; then
echo "$PR_URL remains open for conflicts, failing checks, or review"
exit 0
fi
gh pr view "$PR_URL" --json headRefOid,baseRefOid,state | \
jq -e --arg head "$CANDIDATE_SHA" --arg base "$FORK_MAIN_SHA" '
.headRefOid == $head and .baseRefOid == $base and .state == "OPEN"
' >/dev/null || {
echo "$PR_URL remains open because its tested head or base changed"
exit 0
}
git fetch origin main "$SYNC_BRANCH"
test "$(git rev-parse origin/main)" = "$FORK_MAIN_SHA"
test "$(git rev-parse "origin/$SYNC_BRANCH")" = "$CANDIDATE_SHA"
git merge-base --is-ancestor "$FORK_MAIN_SHA" "$CANDIDATE_SHA"
if git push \
--force-with-lease="refs/heads/main:$FORK_MAIN_SHA" \
origin "$CANDIDATE_SHA:refs/heads/main"; then
echo "Merged compatible upstream sync $PR_URL"
git push origin --delete "$SYNC_BRANCH" || true
else
echo "$PR_URL remains open because main changed after compatibility passed"
fi
- name: Report incompatible sync
if: steps.pr.outputs.url != '' && steps.pr.outputs.compatible != 'true'
env:
PR_URL: ${{ steps.pr.outputs.url }}
run: echo "$PR_URL remains open for conflicts, failing checks, or review"