-
Notifications
You must be signed in to change notification settings - Fork 2
/
action.yml
484 lines (436 loc) · 21.1 KB
/
action.yml
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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
name: Merge up
description: GitHub Action to merge-up supported branches in a repository
runs:
using: composite
steps:
- name: Install PHP
uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2.31.1
with:
php-version: '8.3'
tools: composer:v2
# This is shared between runs, not just jobs. It means the first time the repo runs the job it'll
# need to download requirements for the first time, after that it will be plenty quick
# https://docs.github.com/en/actions/advanced-guides/caching-dependencies-to-speed-up-workflows
- name: Enable shared composer cache
uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # @v4.1.1
with:
path: ~/.cache/composer
key: shared-composer-cache
# Install composer dependencies for this action itself
- name: Composer
shell: bash
run: |
cd ${{ github.action_path }}
composer install
cd -
- name: Determine if should merge-up
id: determine
shell: bash
env:
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_REF_NAME: ${{ github.ref_name }}
run: |
# Get the default branch from GitHub API
# We need to make an API call rather than just assume that the current branch is the default
# because this workflow may be triggered by workflow_dispatch on any branch
# https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#get-a-repository
RESP_CODE=$(curl -w %{http_code} -s -o __base.json \
-X GET "https://api.github.com/repos/$GITHUB_REPOSITORY" \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ github.token }}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
)
if [[ $RESP_CODE != "200" ]]; then
echo "Unable to fetch repository meta data - HTTP response code was $RESP_CODE"
exit 1
fi
DEFAULT_BRANCH=$(jq -r .default_branch __base.json)
echo "DEFAULT_BRANCH is $DEFAULT_BRANCH"
rm __base.json
if [[ $DEFAULT_BRANCH != $GITHUB_REF_NAME ]]; then
echo "Current branch $GITHUB_REF_NAME is not the same as default branch $DEFAULT_BRANCH"
exit 1
fi
# Gets all branches from GitHub API
# https://docs.github.com/en/rest/branches/branches?apiVersion=2022-11-28#list-branches
RESP_CODE=$(curl -w %{http_code} -s -o __branches.json \
-X GET "https://api.github.com/repos/$GITHUB_REPOSITORY/branches?per_page=100" \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ github.token }}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
)
if [[ $RESP_CODE != "200" ]]; then
echo "Unable to read list of branches - HTTP response code was $RESP_CODE"
exit 1
fi
# Gets 100 most recently created tags from GitHub API
# https://docs.github.com/en/rest/git/tags?apiVersion=2022-11-28
RESP_CODE=$(curl -w %{http_code} -s -o __tags.json \
-X GET "https://api.github.com/repos/$GITHUB_REPOSITORY/tags?per_page=100" \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ github.token }}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
)
if [[ $RESP_CODE != "200" ]]; then
echo "Unable to read list of tags - HTTP response code was $RESP_CODE"
exit 1
fi
# Download composer.json for use in branches.php
RESP_CODE=$(curl -w %{http_code} -s -o __composer.json https://raw.githubusercontent.com/$GITHUB_REPOSITORY/$DEFAULT_BRANCH/composer.json)
if [[ $RESP_CODE != 200 ]]; then
rm __composer.json
fi
BRANCHES=$(DEFAULT_BRANCH=$DEFAULT_BRANCH GITHUB_REPOSITORY=$GITHUB_REPOSITORY php ${{ github.action_path }}/branches.php)
echo "BRANCHES is $BRANCHES"
if [[ $BRANCHES =~ "^FAILURE \- (.+)$" ]]; then
MESSAGE=${BASH_REMATCH[1]}
echo "Exception in branches.php - $MESSAGE"
exit 1
fi
if [[ $BRANCHES == "" ]]; then
echo "No branches to merge-up"
exit 0
fi
echo "branches=$BRANCHES" >> $GITHUB_OUTPUT
rm __tags.json
rm __branches.json
if [[ -f __composer.json ]]; then
rm __composer.json
fi
# Check to see if there is anything to merge-up using the GitHub API
# Another approach is to see if we should merged using git, however that approach requires us to
# first checkout the entire git history for the repo first which can use a lot of data, and there may be
# some hidden data rate-limit in GitHub that we don't know about
# These API calls are fast so it really doesn't add much overhead
# One downside to the API approach is that we will abort early and not merge-up anything when we may have been
# able to say merge-up 4.13 -> 4 but not 4 -> 5.0
FROM_BRANCH=""
INTO_BRANCH=""
for BRANCH in $BRANCHES; do
FROM_BRANCH=$INTO_BRANCH
INTO_BRANCH=$BRANCH
if [[ $FROM_BRANCH == "" ]]; then
continue
fi
# https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#compare-two-commits
RESP_CODE=$(curl -w %{http_code} -s -o __compare.json \
-X GET "https://api.github.com/repos/$GITHUB_REPOSITORY/compare/$INTO_BRANCH...$FROM_BRANCH" \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ github.token }}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
)
if [[ $RESP_CODE != "200" ]]; then
echo "Unable to compare branches - HTTP response code was $RESP_CODE"
exit 1
fi
FILES=$(jq -r .files[].filename __compare.json)
rm __compare.json
# Don't allow merge-ups when there are changes in javascript dependency files
DEPENDENCY_FILES="package.json yarn.lock"
for DEPENDENCY_FILE in $DEPENDENCY_FILES; do
if [[ $(echo "$FILES" | grep $DEPENDENCY_FILE) != "" ]]; then
echo "Unable to mergeup $FROM_BRANCH into $INTO_BRANCH - there are changes in $DEPENDENCY_FILE"
exit 1
fi
done
# Don't allow merge-ups where the are changes composer.json, unless the only changes are in "require-dev"
if [[ $(echo "$FILES" | grep composer.json) != "" ]]; then
RESP_CODE=$(curl -w %{http_code} -s -o __composer_from.json https://raw.githubusercontent.com/$GITHUB_REPOSITORY/$FROM_BRANCH/composer.json)
if [[ $RESP_CODE != 200 ]]; then
echo "Unable to download composer.json for branch $FROM_BRANCH - HTTP response code was $RESP_CODE"
exit 1
fi
RESP_CODE=$(curl -w %{http_code} -s -o __composer_into.json https://raw.githubusercontent.com/$GITHUB_REPOSITORY/$INTO_BRANCH/composer.json)
if [[ $RESP_CODE != 200 ]]; then
echo "Unable to download composer.json for branch $INTO_BRANCH - HTTP response code was $RESP_CODE"
exit 1
fi
CAN_MERGE_UP_COMPOSER_JSON=$(php -r '
$from = json_decode(file_get_contents("__composer_from.json"), true);
$into = json_decode(file_get_contents("__composer_into.json"), true);
if (!$from) {
throw new Exception("Could not parse __composer_from.json - " . json_last_error_msg());
}
if (!$into) {
throw new Exception("Could not parse __composer_into.json - " . json_last_error_msg());
}
if (array_key_exists("require-dev", $from)) {
unset($from["require-dev"]);
}
if (array_key_exists("require-dev", $into)) {
unset($into["require-dev"]);
}
echo json_encode($from) === json_encode($into) ? "1" : "0";
')
rm __composer_from.json
rm __composer_into.json
if [[ $CAN_MERGE_UP_COMPOSER_JSON == "0" ]]; then
echo "Unable to mergeup $FROM_BRANCH into $INTO_BRANCH - there are non require-dev changes in composer.json"
exit 1
fi
fi
done
# actions/checkout with fetch-depth: 0 will fetch ALL git history for the repository
# this is required for a merge-up to ensure that nothing is missed
- name: Checkout code
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
with:
fetch-depth: 0
- name: Git merge-up
id: git-merge-up
shell: bash
env:
BRANCHES: ${{ steps.determine.outputs.branches }}
GITHUB_REPOSITORY: ${{ github.repository }}
run: |
# Set git user to github-actions bot
# The 41898282+ email prefixed is the required, matches the ID here
# https://api.github.com/users/github-actions%5Bbot%5D
# https://github.community/t/github-actions-bot-email-address/17204/6
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions"
# List of branches to trigger-ci for later
# Normally we'll only ever trigger 3 branches, though providing up to 6 for scenarios
# where there is are things like betas in the mix
# branches.php will throw an Exception early if there is more than 6 branches which will cause
# this job to fail without any merge-ups being performed
TRIGGER_CI_BRANCH_01=""
TRIGGER_CI_BRANCH_02=""
TRIGGER_CI_BRANCH_03=""
TRIGGER_CI_BRANCH_04=""
TRIGGER_CI_BRANCH_05=""
TRIGGER_CI_BRANCH_06=""
TRIGGER_CI_EXCEEDED=""
FROM_BRANCH=""
INTO_BRANCH=""
for BRANCH in $BRANCHES; do
FROM_BRANCH=$INTO_BRANCH
INTO_BRANCH=$BRANCH
if [[ $FROM_BRANCH == "" ]]; then
continue
fi
echo "Attempting to merge-up $FROM_BRANCH into $INTO_BRANCH"
# Checkout both branches to ensure branch info is up to date
# Also runs `git pull` which should not be necessary, though in practice GitHub actions sometimes
# rejects when pushing saying the tip of the branch is out of date. This possibly only happens
# when running a failed job though that's still a valid scenario
git checkout $FROM_BRANCH
git pull
FROM_BRANCH_SHA=$(git rev-parse HEAD)
echo "FROM_BRANCH $FROM_BRANCH sha is $FROM_BRANCH_SHA"
git checkout $INTO_BRANCH
git pull
INTO_BRANCH_SHA=$(git rev-parse HEAD)
echo "INTO_BRANCH $INTO_BRANCH sha is $INTO_BRANCH_SHA"
# If both branches are on the same sha, there's nothing to do
if [[ $FROM_BRANCH_SHA == $INTO_BRANCH_SHA ]]; then
echo "FROM_BRANCH $FROM_BRANCH and INTO_BRANCH $INTO_BRANCH have the same sha. Skipping."
continue
fi
# Determine if we will rebuild dist file during merge-up
# This is based simply on if there are changes in the client/ directory and if there's a package.json file
REBUILD=0
CLIENT_DIFF_FILES=$(git diff --name-only $INTO_BRANCH...$FROM_BRANCH | grep -P ^client/) || true
if [[ $CLIENT_DIFF_FILES != "" && -f "package.json" ]]; then
REBUILD=1
fi
echo "CLIENT_DIFF_FILES is:"
# The following line is quoted so that newlines show
echo "$CLIENT_DIFF_FILES"
echo "REBUILD is $REBUILD"
# Perform the merge-up
# `|| true is suffixed to the command to prevent the job from stopping on merge conflict
# This is because git will sent a non-zero exit code when there is a merge conflict
# We often expect a merge-conflict when there are client/dist file differences
MERGE_RESULT=$(git merge --no-ff --no-commit $FROM_BRANCH || true)
# Only merge conflicts in client/dist are allowed, stop for all others
# See https://git-scm.com/docs/git-status#_output for information on the porcelain format
UNMERGED_FILES=$(git status --porcelain=v1 | grep -P '^(DD|AU|UD|UA|DU|AA|UU)' | grep -v client/dist) || true
if [[ $UNMERGED_FILES != "" ]]; then
echo "Merge conflict found when merging-up $FROM_BRANCH into $INTO_BRANCH. Aborting."
# The following line needs to be quoted so that line breaks show
echo "$UNMERGED_FILES"
exit 1
fi
# Rebuild client/dist if needed
if [[ $REBUILD == 1 ]]; then
# Ensure .nvmrc is present
if ! [[ -f .nvmrc ]]; then
echo "Unable to find .nvmrc file"
exit 1
fi
# Piping into xargs trims out whitespace
NVM_VERSION=$(cat .nvmrc | xargs)
echo "NVM_VERSION is $NVM_VERSION"
# Ensure nvm is installed
if [[ $(which nvm) == "" ]]; then
wget -q -O __nvm_install.sh https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.4/install.sh
if [[ $(sha1sum __nvm_install.sh) != "c10365646e699a74e279e6ae71569430f82ba014 __nvm_install.sh" ]]; then
"Unable to verify integrity of nvm install script"
exit 1
fi
. __nvm_install.sh
rm __nvm_install.sh
# Load nvm without needing terminal restart
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
fi
# Install correct version of node and yarn
nvm install $NVM_VERSION
nvm use $NVM_VERSION
if [[ $(which yarn) == "" ]]; then
npm install -g yarn
fi
# Get composer name of the module
COMPOSER_NAME=$(jq -r ".name" composer.json)
# Install silverstripe/admin in sibling directory if needed so that shared components are available
if [[ $COMPOSER_NAME != silverstripe/admin ]]; then
DIR=$(pwd)
# Work out version of silverstripe/admin to checkout
cd ..
mkdir __tmp
cd __tmp
ADMIN_VERSION=$(COMPOSER_NAME=$COMPOSER_NAME INTO_BRANCH=$INTO_BRANCH php -r '
$COMPOSER_NAME = getenv("COMPOSER_NAME");
$INTO_BRANCH = getenv("INTO_BRANCH");
$json = [
"require" => [
"$COMPOSER_NAME" => "$INTO_BRANCH.x-dev",
"silverstripe/recipe-kitchen-sink" => "*"
],
"prefer-stable" => false,
"minimum-stability" => "dev"
];
file_put_contents("composer.json", json_encode($json, JSON_UNESCAPED_SLASHES));
shell_exec("composer update --no-install");
$lock = json_decode(file_get_contents("composer.lock"), true);
$version = array_values(array_filter(
$lock["packages"],
fn($p) => $p["name"] === "silverstripe/admin"
))[0]["version"];
echo str_replace(".x-dev", "", $version);
')
echo "ADMIN_VERSION is $ADMIN_VERSION"
if ! [[ $ADMIN_VERSION =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
echo "Unable to determine major or minor branch of silverstripe/admin version to checkout"
exit 1
fi
cd ..
rm -rf __tmp
# Install admin if required
if [[ ! -d admin ]]; then
git clone https://github.com/silverstripe/silverstripe-admin.git admin
fi
# Checkout admin version
cd admin
git checkout $ADMIN_VERSION
yarn install
cd $DIR
fi
# Rebuild dist files
yarn build
# Only add client files
git add client/dist
fi
# See https://git-scm.com/docs/git-status#_output for information on the porcelain format
GIT_STATUS=$(git status --porcelain=v1)
# Check for any unmerged files
UNMERGED_FILES=$(echo "$GIT_STATUS" | grep -P '^(DD|AU|UD|UA|DU|AA|UU)') || true
if [[ $UNMERGED_FILES != "" ]]; then
echo "Unmerged files found when merging-up $FROM_BRANCH into $INTO_BRANCH. Aborting."
echo "$UNMERGED_FILES"
exit 1
fi
# Check for any random untracked files that shouldn't be committed
UNTRACKED_FILES=$(echo "$GIT_STATUS" | grep -P '^\?') || true
if [[ $UNTRACKED_FILES != "" ]]; then
echo "Untracked files found when merging-up $FROM_BRANCH into $INTO_BRANCH. Aborting."
echo "$UNTRACKED_FILES"
exit 1
fi
# Things are update to up to date, so do not commit
if [[ $MERGE_RESULT =~ "Already up to date" ]]; then
echo "Merge result said things are already up to date when merging-up $FROM_BRANCH into $INTO_BRANCH. Skipping."
continue
fi
# Commit and push the merge-up
# The quotes in the commit message are intential and match the auto-generated
# title of e.g. Merge branch '2.0' into 2
# Using this instead of simply `git status --no-edit` which auto-generates a commit message because
# that includes commented out details of the merge conflict if there was one
git commit -m "Merge branch '$FROM_BRANCH' into $INTO_BRANCH"
git push origin $INTO_BRANCH
echo "Succesfully merged-up $FROM_BRANCH into $INTO_BRANCH"
if [[ $TRIGGER_CI_BRANCH_01 == "" ]]; then
TRIGGER_CI_BRANCH_01=$INTO_BRANCH
elif [[ $TRIGGER_CI_BRANCH_02 == "" ]]; then
TRIGGER_CI_BRANCH_02=$INTO_BRANCH
elif [[ $TRIGGER_CI_BRANCH_03 == "" ]]; then
TRIGGER_CI_BRANCH_03=$INTO_BRANCH
elif [[ $TRIGGER_CI_BRANCH_04 == "" ]]; then
TRIGGER_CI_BRANCH_04=$INTO_BRANCH
elif [[ $TRIGGER_CI_BRANCH_05 == "" ]]; then
TRIGGER_CI_BRANCH_05=$INTO_BRANCH
elif [[$TRIGGER_CI_BRANCH_06 == "" ]]; then
TRIGGER_CI_BRANCH_06=$INTO_BRANCH
else
if [[ $TRIGGER_CI_EXCEEDED == "" ]]; then
TRIGGER_CI_EXCEEDED=$INTO_BRANCH
else
TRIGGER_CI_EXCEEDED="$TRIGGER_CI_EXCEEDED,$INTO_BRANCH"
fi
fi
done
echo "TRIGGER_CI_BRANCH_01 is $TRIGGER_CI_BRANCH_01"
echo "TRIGGER_CI_BRANCH_02 is $TRIGGER_CI_BRANCH_02"
echo "TRIGGER_CI_BRANCH_03 is $TRIGGER_CI_BRANCH_03"
echo "TRIGGER_CI_BRANCH_04 is $TRIGGER_CI_BRANCH_04"
echo "TRIGGER_CI_BRANCH_05 is $TRIGGER_CI_BRANCH_05"
echo "TRIGGER_CI_BRANCH_06 is $TRIGGER_CI_BRANCH_06"
echo "TRIGGER_CI_EXCEEDED is $TRIGGER_CI_EXCEEDED"
echo "trigger_ci_branch_01=$TRIGGER_CI_BRANCH_01" >> $GITHUB_OUTPUT
echo "trigger_ci_branch_02=$TRIGGER_CI_BRANCH_02" >> $GITHUB_OUTPUT
echo "trigger_ci_branch_03=$TRIGGER_CI_BRANCH_03" >> $GITHUB_OUTPUT
echo "trigger_ci_branch_04=$TRIGGER_CI_BRANCH_04" >> $GITHUB_OUTPUT
echo "trigger_ci_branch_05=$TRIGGER_CI_BRANCH_05" >> $GITHUB_OUTPUT
echo "trigger_ci_branch_06=$TRIGGER_CI_BRANCH_06" >> $GITHUB_OUTPUT
echo "trigger_ci_exceeded=$TRIGGER_CI_EXCEEDED" >> $GITHUB_OUTPUT
- name: Trigger CI 01
if: ${{ steps.git-merge-up.outputs.trigger_ci_branch_01 != '' }}
uses: silverstripe/gha-trigger-ci@v1
with:
branch: ${{ steps.git-merge-up.outputs.trigger_ci_branch_01 }}
- name: Trigger CI 02
if: ${{ steps.git-merge-up.outputs.trigger_ci_branch_02 != '' }}
uses: silverstripe/gha-trigger-ci@v1
with:
branch: ${{ steps.git-merge-up.outputs.trigger_ci_branch_02 }}
- name: Trigger CI 03
if: ${{ steps.git-merge-up.outputs.trigger_ci_branch_03 != '' }}
uses: silverstripe/gha-trigger-ci@v1
with:
branch: ${{ steps.git-merge-up.outputs.trigger_ci_branch_03 }}
- name: Trigger CI 04
if: ${{ steps.git-merge-up.outputs.trigger_ci_branch_04 != '' }}
uses: silverstripe/gha-trigger-ci@v1
with:
branch: ${{ steps.git-merge-up.outputs.trigger_ci_branch_04 }}
- name: Trigger CI 05
if: ${{ steps.git-merge-up.outputs.trigger_ci_branch_05 != '' }}
uses: silverstripe/gha-trigger-ci@v1
with:
branch: ${{ steps.git-merge-up.outputs.trigger_ci_branch_05 }}
- name: Trigger CI 06
if: ${{ steps.git-merge-up.outputs.trigger_ci_branch_06 != '' }}
uses: silverstripe/gha-trigger-ci@v1
with:
branch: ${{ steps.git-merge-up.outputs.trigger_ci_branch_06 }}
- name: Trigger CI Exceeded
if: ${{ steps.git-merge-up.outputs.trigger_ci_exceeded != '' }}
shell: bash
env:
TRIGGER_CI_EXCEEDED: ${{ steps.git-merge-up.outputs.trigger_ci_exceeded }}
run: |
echo "Exceeded the number of branches that can be have CI triggered"
echo "Branches where CI was not triggered is $TRIGGER_CI_EXCEEDED"
exit 1