#!/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 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" 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 --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 # Update the manifest again to reach a fixed state (https://github.com/servo/servo/issues/22275). ./mach update-manifest || return 4 # Amend the existing commit with the new changes from updating the manifest. git commit -a --amend --no-edit || return 5 } # 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 6 --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 # Ensure any new directories or ini files are included in these changes. git add tests/wpt/metadata tests/wpt/mozilla/meta || return 2 # 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}" &>/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]" 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 5 } 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 elif [[ "${code}" != "" ]]; then return "${code}" 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}"