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
|
#!/usr/bin/env bash
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
set -o errexit
set -o nounset
set -o pipefail
REMOTE_NAME=sync-fork
LOG_FILE=test-wpt.log
BLUETOOTH_LOG_FILE=test-wpt-bluetooth.log
WDSPEC_LOG_FILE=test-wpt-wdspec.log
CURRENT_DATE=$(date +"%d-%m-%Y")
BRANCH_NAME="wpt_update"
REMOTE_BRANCH_NAME="wpt_update_${CURRENT_DATE}"
export GIT_AUTHOR_NAME="WPT Sync Bot"
export GIT_AUTHOR_EMAIL="josh+wptsync@joshmatthews.net"
export GIT_COMMITTER_NAME="${GIT_AUTHOR_NAME}"
export GIT_COMMITTER_EMAIL="${GIT_AUTHOR_EMAIL}"
# Retrieve the HEAD commit and extract its hash
function latest_git_commit() {
git log -1 --oneline | cut -f 1 -d ' '
}
# Create a new branch for this sync, pull down all changes from the upstream
# web-platform-tests repository, and commit the changes.
function unsafe_pull_from_upstream() {
git checkout -b "${1}" || return 1
OLD_COMMIT=$(latest_git_commit)
# Fetch all changes from upstream WPT and automatically transpose them
# into a single servo commit.
./mach update-wpt --sync --patch || return 2
# If there was no new commit created, there are no changes that need syncing.
# Skip the remaining steps.
if [[ "$(latest_git_commit)" == "${OLD_COMMIT}" ]]; then
return 255
fi
# Update the manifest to include the new changes.
./mach update-manifest || return 3
# Update the manifest again to reach a fixed state (https://github.com/servo/servo/issues/22275).
./mach update-manifest || return 4
# Clean up any now-unused test results.
(python3 etc/unused_wpt_results.py | xargs rm -rfv) || return 5
# Amend the existing commit with the new changes from updating the manifest.
git commit -a --amend --no-edit || return 6
}
# Remove all local traces of this sync operation.
function cleanup() {
git remote rm "${REMOTE_NAME}" || true
git reset --hard || true
git checkout master || true
git branch -D "${BRANCH_NAME}" || true
./mach update-wpt --abort || true
}
# Build Servo and run the full WPT testsuite, saving the results to a log file.
function unsafe_run_tests() {
# Run the full testsuite and record the new test results.
./mach test-wpt --release --processes 12 --log-raw "${1}" \
--always-succeed || return 1
# Run the bluetooth testsuite, which uses the webdriver test harness.
./mach test-wpt --release --product=servodriver --headless \
--log-raw "${2}" /bluetooth --always-succeed || return 2
# Run the wdspec testsuite
./mach test-wpt --release --timeout-multiplier=4 --log-raw "${3}" /webdriver \
--always-succeed || return 3
}
# Using an existing log file, update the expected test results and amend the
# last commit with the new results.
function unsafe_update_metadata() {
./mach update-wpt "${1}" "${2}" "${3}" || return 1
# Hope that any test result changes from layout-2013 are also applicable to layout-2020.
./mach update-wpt --layout-2020 "${1}" "${2}" "${3}" || return 2
# Ensure any new directories or ini files are included in these changes.
git add tests/wpt/metadata tests/wpt/metadata-layout-2020 tests/wpt/mozilla/meta || return 3
# Merge all changes with the existing commit.
git commit -a --amend --no-edit || return 3
}
# Push the branch to a remote branch, then open a PR for the branch
# against servo/servo.
function unsafe_open_pull_request() {
WPT_SYNC_USER=servo-wpt-sync
# If the branch doesn't exist, we'll silently exit. This deals with the
# case where an earlier step either failed or discovered that syncing
# is unnecessary.
git checkout "${BRANCH_NAME}" || return 0
if [[ -z "${WPT_SYNC_TOKEN+set}" ]]; then
echo "Github auth token missing from WPT_SYNC_TOKEN."
return 1
fi
# Push the changes to a remote branch owned by the bot.
AUTH="${WPT_SYNC_USER}:${WPT_SYNC_TOKEN}"
UPSTREAM="https://${AUTH}@github.com/${WPT_SYNC_USER}/servo.git"
git remote add "${REMOTE_NAME}" "${UPSTREAM}" || return 2
git push -f "${REMOTE_NAME}" "${BRANCH_NAME}:${REMOTE_BRANCH_NAME}" &>/dev/null || return 3
# Prepare the pull request metadata.
BODY="Automated downstream sync of changes from upstream as of "
BODY+="${CURRENT_DATE}.\n"
BODY+="[no-wpt-sync]\n"
BODY+="r? @servo-wpt-sync\n"
cat <<EOF >prdata.json || return 4
{
"title": "Sync WPT with upstream (${CURRENT_DATE})",
"head": "${WPT_SYNC_USER}:${REMOTE_BRANCH_NAME}",
"base": "master",
"body": "${BODY}",
"maintainer_can_modify": true
}
EOF
# Open a pull request using the new branch.
OPEN_PR_RESPONSE=$(curl -H "Authorization: token ${WPT_SYNC_TOKEN}" \
-H "Content-Type: application/json" \
--data @prdata.json \
https://api.github.com/repos/servo/servo/pulls) || return 5
echo "${OPEN_PR_RESPONSE}" | \
jq '.review_comments_url' | \
sed 's/pulls/issues/' | \
xargs curl -H "Authorization: token ${WPT_SYNC_TOKEN}" \
--data "{\"body\":\"@bors-servo r+\"}" || return 6
}
function pull_from_upstream() {
unsafe_pull_from_upstream "${1}" || { code="${?}"; cleanup; return "${code}"; }
}
function run_tests() {
unsafe_run_tests "${1}" "${2}" "${3}" || { code="${?}"; cleanup; return "${code}"; }
}
function update_metadata() {
unsafe_update_metadata "${1}" "${2}" "${3}" || { code="${?}"; cleanup; return "${code}"; }
}
function open_pull_request() {
unsafe_open_pull_request || { code="${?}"; cleanup; return "${code}"; }
}
SCRIPT_NAME="${0}"
function update_test_results() {
run_tests "${LOG_FILE}" "${BLUETOOTH_LOG_FILE}" "${WDSPEC_LOG_FILE}"
update_metadata "${LOG_FILE}" "${BLUETOOTH_LOG_FILE}" "${WDSPEC_LOG_FILE}"
}
function fetch_upstream_changes() {
pull_from_upstream "${BRANCH_NAME}"
}
function usage() {
echo "usage: ${SCRIPT_NAME} [cmd]"
echo " commands:"
echo " - fetch-upstream-changes: create a branch with the latest changes from upstream"
echo " - update-test-results: run the tests, update the expected test results, and commit the changes"
echo " - fetch-and-update-expectations: combines fetch-upstream-changes and update-test-results"
echo " - open-pr: open a pull request with the latest changes"
echo " - cleanup: cleanup all traces of an in-progress sync and checkout the master branch"
exit 1
}
function main() {
if [[ "${1}" == "fetch-upstream-changes" ]] || [[ "${1}" == "fetch-and-update-expectations" ]]; then
code=""
fetch_upstream_changes || code="${?}"
if [[ "${code}" == "255" ]]; then
echo "No changes to sync."
return 0
fi
if [[ "${code}" != "" ]]; then
return "${code}"
else
return 0
fi
fi
if [[ "${1}" == "update-test-results" ]] || [[ "${1}" == "fetch-and-update-expectations" ]]; then
update_test_results
elif [[ "${1}" == "open-pr" ]]; then
open_pull_request
elif [[ "${1}" == "cleanup" ]]; then
cleanup
else
usage
fi
}
if [[ "$#" != 1 ]]; then
usage
fi
# Ensure we clean up after ourselves if this script is interrupted.
trap 'cleanup' SIGINT SIGTERM
main "${1}"
|