-
Notifications
You must be signed in to change notification settings - Fork 40
178 lines (159 loc) · 7.18 KB
/
Copy pathsplit.yml
File metadata and controls
178 lines (159 loc) · 7.18 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: Split Packages
on:
push:
branches: [main, develop]
tags: ['[0-9]*']
concurrency:
group: split-${{ github.ref }}
cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }}
env:
SPLIT_ORG: marko-php
jobs:
setup:
runs-on: ubuntu-latest
outputs:
packages: ${{ steps.list.outputs.packages }}
steps:
- uses: actions/checkout@v6
- name: Create missing split repos
env:
GH_TOKEN: ${{ secrets.MARKO_BUILD_PAT }}
run: ./bin/create-split-repos.sh
- name: List packages
id: list
run: |
packages=$(ls packages/ | jq -R -s -c 'split("\n") | map(select(length > 0))')
echo "packages=$packages" >> $GITHUB_OUTPUT
split:
needs: setup
runs-on: ubuntu-latest
strategy:
matrix:
package: ${{ fromJson(needs.setup.outputs.packages) }}
fail-fast: false
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: false
- name: Configure git
run: |
git config --global user.email "action@github.com"
git config --global user.name "GitHub Action"
- name: Install splitsh-lite
run: |
curl -sL https://github.com/splitsh/lite/releases/download/v1.0.1/lite_linux_amd64.tar.gz | tar xz
sudo mv splitsh-lite /usr/local/bin/splitsh-lite
- name: Split and push
env:
MARKO_BUILD_PAT: ${{ secrets.MARKO_BUILD_PAT }}
run: |
REPO="https://x-access-token:${MARKO_BUILD_PAT}@github.com/${SPLIT_ORG}/marko-${{ matrix.package }}.git"
SHA=$(splitsh-lite --prefix="packages/${{ matrix.package }}")
# During a release, release.sh pushes main, the tag, and develop in
# quick succession, so the main-run and tag-run race to push the same
# split repo's refs/heads/main and one loses the atomic ref-lock
# ("cannot lock ref ... expected <old>"). --force does not help: the
# collision is the ref-lock, not a fast-forward. Retry instead — the
# split SHA is deterministic, so a retry finds the ref already at the
# target SHA and succeeds ("Everything up-to-date").
push() {
for attempt in 1 2 3; do
git push --force "$REPO" "${SHA}:$1" && return 0
echo "push to $1 failed (attempt $attempt/3)"
[ "$attempt" -lt 3 ] && sleep "$attempt"
done
return 1
}
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
TAG="${GITHUB_REF#refs/tags/}"
push "refs/heads/main"
push "refs/tags/${TAG}"
else
BRANCH="${GITHUB_REF#refs/heads/}"
push "refs/heads/${BRANCH}"
fi
- name: Notify Packagist
if: startsWith(github.ref, 'refs/tags/')
env:
PACKAGIST_TOKEN: ${{ secrets.PACKAGIST_TOKEN }}
run: |
PKG_URL="https://github.com/${SPLIT_ORG}/marko-${{ matrix.package }}"
RESP_FILE=/tmp/packagist_resp
# Every package in the matrix reaches this step within the same second,
# so a release fires ~92 update-package calls at Packagist at once and
# reliably trips its limits. On 0.8.5 that returned HTTP 500 for eight
# packages; the tags had already pushed, so the job failed loudly while
# the only real damage was a stale Packagist index — including
# marko/core, which everything else resolves against.
#
# Retry the transient classes and let the definitive ones fall through
# to the caller: 404 means "not registered" and drives the create-package
# self-heal below, and 401/403 mean a bad token, which no retry fixes.
# HTTP is global rather than echoed so the log lines below can't be
# captured as part of the status code.
HTTP=""
update_with_retry() {
local attempt delay
for attempt in 1 2 3 4 5; do
# curl leaves the response file untouched when it cannot connect,
# so clear it first — otherwise a transport failure reports the
# previous attempt's body as if it were this one's.
: > "$RESP_FILE"
# `|| true` keeps a transport failure from aborting the step under
# `bash -e` so it can be retried; the status is validated below.
HTTP=$(curl -s -o "$RESP_FILE" -w "%{http_code}" \
--connect-timeout 10 --max-time 60 \
-X POST https://packagist.org/api/update-package \
-H "Content-Type: application/json" \
-H "Authorization: Bearer markshust:${PACKAGIST_TOKEN}" \
-d "{\"repository\":\"${PKG_URL}\"}") || true
case "$HTTP" in
000|429|5??) ;; # transient — worth another attempt
*) return 0 ;; # definitive — caller decides what it means
esac
echo "Packagist update returned HTTP ${HTTP} (attempt ${attempt}/5)"
head -c 500 "$RESP_FILE" 2>/dev/null || true
echo
# Jittered backoff. Without the jitter every job would have failed
# on the same second and would retry on the same second too.
if [[ "$attempt" -lt 5 ]]; then
delay=$(( 2 ** attempt + RANDOM % 5 ))
echo "Retrying in ${delay}s..."
sleep "$delay"
fi
done
return 0
}
# Try to update first. If Packagist returns 404 the package isn't
# registered yet — self-heal by calling create-package, then retry
# the update so the new tag actually gets indexed. This removes
# the manual `register-packagist.sh` step from the new-package
# workflow: contributors can add `packages/foo/` via PR and the
# first release tag will register + publish it automatically.
update_with_retry
if [[ "$HTTP" == "404" ]]; then
echo "marko/${{ matrix.package }} not registered on Packagist — registering now..."
curl -sf -X POST \
"https://packagist.org/api/create-package?username=markshust&apiToken=${PACKAGIST_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"repository\":{\"url\":\"${PKG_URL}\"}}"
echo "Registered. Re-running update so the tag gets indexed..."
update_with_retry
fi
# Match on the success class rather than testing `-ge 400`. The curl
# above no longer aborts the step on a transport failure, so that case
# now reaches here as 000 — numerically zero, and therefore something
# a `-ge 400` test would wave through as success, leaving the package
# stale behind a green job.
case "$HTTP" in
2??)
echo "marko/${{ matrix.package }} updated on Packagist (HTTP ${HTTP})"
;;
*)
echo "Packagist update failed with HTTP ${HTTP} after 5 attempts:"
cat "$RESP_FILE" 2>/dev/null || true
echo
exit 1
;;
esac