diff --git a/etc/ci/buildbot_steps.yml b/etc/ci/buildbot_steps.yml index b80ed719e1f8..fa0ba8a2226b 100644 --- a/etc/ci/buildbot_steps.yml +++ b/etc/ci/buildbot_steps.yml @@ -73,6 +73,9 @@ mac-nightly: - env PKG_CONFIG_PATH=/usr/local/opt/zlib/lib/pkgconfig ./mach build --release - ./mach package --release - ./mach upload-nightly mac + - ./etc/ci/update-wpt-checkout fetch-and-update-expectations + - ./etc/ci/update-wpt-checkout open-pr + - ./etc/ci/update-wpt-checkout cleanup linux-rel-intermittent: - ./mach clean-nightlies --keep 3 --force diff --git a/etc/ci/update-wpt-checkout b/etc/ci/update-wpt-checkout new file mode 100755 index 000000000000..6203d4641917 --- /dev/null +++ b/etc/ci/update-wpt-checkout @@ -0,0 +1,186 @@ +#!/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 http://mozilla.org/MPL/2.0/. + +set -o errexit +set -o nounset +set -o pipefail + +REMOTE_NAME=sync-fork +LOG_FILE=test-wpt.log +CURRENT_DATE=$(date +"%d-%m-%Y") +BRANCH_NAME="wpt_update_${CURRENT_DATE}" + +export GIT_AUTHOR_NAME="WPT Sync Bot" +export GIT_AUTHOR_EMAIL="josh+wptsync@joshmatthews.net" + +# 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 --no-upstream --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 + + # Amend the existing commit with the new changes from updating the manifest. + git commit -a --amend --no-edit || return 4 +} + +# 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 24 --log-raw "${1}" \ + --always-succeed || return 1 +} + +# 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}" || return 1 + # Ignore any metadata changes to files that weren't modified by this sync. + git checkout -- tests/wpt/mozilla/meta || return 2 + # Ensure any new directories or ini files are included in these changes. + git add tests/wpt/metadata || return 3 + # Merge all changes with the existing commit. + git commit -a --amend --no-edit || return 4 +} + +# 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}" || 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]" + cat <prdata.json || return 4 +{ + "title": "Sync WPT with upstream (${CURRENT_DATE})", + "head": "${WPT_SYNC_USER}:${BRANCH_NAME}", + "base": "master", + "body": "${BODY}", + "maintainer_can_modify": true +} +EOF + + # Open a pull request using the new branch. + curl -H "Authorization: token ${WPT_SYNC_TOKEN}" \ + -H "Content-Type: application/json" \ + --data @prdata.json \ + https://api.github.com/repos/servo/servo/pulls || return 4 +} + +function pull_from_upstream() { + unsafe_pull_from_upstream "${1}" || { code="${?}"; cleanup; return "${code}"; } +} + +function run_tests() { + unsafe_run_tests "${1}" || { code="${?}"; cleanup; return "${code}"; } +} + +function update_metadata() { + unsafe_update_metadata "${1}" || { 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}" + update_metadata "${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 + 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}"