[ci] Remove deployment of PRs to wptpr.live (#28055)
diff --git a/.github/workflows/pull_request_previews.yml b/.github/workflows/pull_request_previews.yml
deleted file mode 100644
index 676aff3..0000000
--- a/.github/workflows/pull_request_previews.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-# Create previews for pull requests on https://wptpr.live
-#
-# Mirroring pull requests to wptpr.live requires write access to the WPT GitHub
-# repository. This means that we cannot run any code from the pull request in
-# doing so, to defend against attacks from malicious pull requests. As such,
-# this workflow uses the 'pull_request_target' event which runs in the context
-# of the base repository and thus doesn't run code from the pull request. Any
-# code run in this workflow should NOT checkout or trust code from the pull
-# request.
-#
-# Note that in pull_request_target the GITHUB token is read/write.
-name: create-pr-preview
-on:
- pull_request_target:
- types: [opened, synchronize, reopened, closed, labeled, unlabeled]
-jobs:
- update-pr-preview:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v1
- with:
- fetch-depth: 1
- - name: Install dependency
- run: pip install requests
- - name: Deploy PR
- # Use a conditional step instead of a conditional job to work around #20700.
- if: github.repository == 'web-platform-tests/wpt'
- run:
- ./tools/ci/pr_preview.py
- --host https://api.github.com
- --github-project web-platform-tests/wpt
- --target https://wptpr.live
- --timeout 600
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/tools/ci/pr_preview.py b/tools/ci/pr_preview.py
deleted file mode 100755
index c6932da..0000000
--- a/tools/ci/pr_preview.py
+++ /dev/null
@@ -1,378 +0,0 @@
-#!/usr/bin/env python
-
-# The service provided by this script is not critical, but it shares a GitHub
-# API request quota with critical services. For this reason, all requests to
-# the GitHub API are preceded by a "guard" which verifies that the subsequent
-# request will not deplete the shared quota.
-#
-# In effect, this script will fail rather than interfere with the operation of
-# critical services.
-
-import argparse
-import json
-import logging
-import os
-import time
-
-import requests
-
-# The ratio of "requests remaining" to "total request quota" below which this
-# script should refuse to interact with the GitHub.com API
-API_RATE_LIMIT_THRESHOLD = 0.2
-# The GitHub Pull Request label which indicates that a Pull Request is expected
-# to be actively mirrored by the preview server
-LABEL = 'safe for preview'
-# The number of seconds to wait between attempts to verify that a submission
-# preview is available on the Pull Request preview server
-POLLING_PERIOD = 15
-# Pull Requests from authors with the following associations to the project
-# should automatically receive previews
-#
-# https://developer.github.com/v4/enum/commentauthorassociation/ (equivalent
-# documentation for the REST API was not available at the time of writing)
-TRUSTED_AUTHOR_ASSOCIATIONS = ('COLLABORATOR', 'MEMBER', 'OWNER')
-# These GitHub accounts are not associated with individuals, and the Pull
-# Requests they submit rarely require a preview.
-AUTOMATION_GITHUB_USERS = (
- 'autofoolip', 'chromium-wpt-export-bot', 'moz-wptsync-bot',
- 'servo-wpt-sync'
-)
-DEPLOYMENT_PREFIX = 'wpt-preview-'
-
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger(__name__)
-
-def gh_request(method_name, url, body=None, media_type=None):
- github_token = os.environ['GITHUB_TOKEN']
-
- kwargs = {
- 'headers': {
- 'Authorization': 'token {}'.format(github_token),
- 'Accept': media_type or 'application/vnd.github.v3+json'
- }
- }
- method = getattr(requests, method_name.lower())
-
- if body is not None:
- kwargs['json'] = body
-
- logger.info('Issuing request: %s %s', method_name.upper(), url)
-
- resp = method(url, **kwargs)
-
- logger.info('Response status code: %s', resp.status_code)
-
- # If GitHub thinks the fields are invalid, it will send a 422 back and
- # include debugging information in the body. See
- # https://developer.github.com/v3/#client-errors
- if resp.status_code == 422:
- logger.error(resp.json())
-
- resp.raise_for_status()
-
- if resp.status_code == 204:
- return None
- return resp.json()
-
-class GitHubRateLimitException(Exception):
- pass
-
-def guard(resource):
- '''Decorate a `Project` instance method which interacts with the GitHub
- API, ensuring that the subsequent request will not deplete the relevant
- allowance. This verification does not itself influence rate limiting:
-
- > Accessing this endpoint does not count against your REST API rate limit.
-
- https://developer.github.com/v3/rate_limit/
- '''
- def guard_decorator(func):
- def wrapped(self, *args, **kwargs):
- limits = gh_request('GET', '{}/rate_limit'.format(self._host))
-
- values = limits['resources'].get(resource)
-
- remaining = values['remaining']
- limit = values['limit']
-
- logger.info(
- 'Limit for "%s" resource: %s/%s', resource, remaining, limit
- )
-
- if limit and float(remaining) / limit < API_RATE_LIMIT_THRESHOLD:
- raise GitHubRateLimitException(
- 'Exiting to avoid GitHub.com API request throttling.'
- )
-
- return func(self, *args, **kwargs)
- return wrapped
- return guard_decorator
-
-class Project(object):
- def __init__(self, host, github_project):
- self._host = host
- self._github_project = github_project
-
- @guard('core')
- def create_ref(self, refspec, revision):
- url = '{}/repos/{}/git/refs'.format(self._host, self._github_project)
-
- logger.info('Creating ref "%s" (%s)', refspec, revision)
-
- gh_request('POST', url, {
- 'ref': 'refs/{}'.format(refspec),
- 'sha': revision
- })
-
- @guard('core')
- def get_ref_revision(self, refspec):
- url = '{}/repos/{}/git/refs/{}'.format(
- self._host, self._github_project, refspec
- )
-
- logger.info('Fetching ref "%s"', refspec)
-
- try:
- body = gh_request('GET', url)
- logger.info('Ref data: %s', json.dumps(body, indent=2))
- return body['object']['sha']
- except requests.exceptions.HTTPError as e:
- if e.response.status_code == 404:
- return None
- raise e
-
- @guard('core')
- def update_ref(self, refspec, revision):
- url = '{}/repos/{}/git/refs/{}'.format(
- self._host, self._github_project, refspec
- )
-
- logger.info('Updating ref "%s" (%s)', refspec, revision)
-
- gh_request('PATCH', url, {'sha': revision})
-
- @guard('core')
- def delete_ref(self, refspec):
- url = '{}/repos/{}/git/refs/{}'.format(
- self._host, self._github_project, refspec
- )
-
- logger.info('Deleting ref "%s"', refspec)
-
- gh_request('DELETE', url)
-
- @guard('core')
- def create_deployment(self, pull_request, revision):
- url = '{}/repos/{}/deployments'.format(
- self._host, self._github_project
- )
- # The Pull Request preview system only exposes one Deployment for a
- # given Pull Request. Identifying the Deployment by the Pull Request
- # number ensures that GitHub.com automatically responds to new
- # Deployments by designating prior Deployments as "inactive"
- environment = DEPLOYMENT_PREFIX + str(pull_request['number'])
-
- logger.info('Creating Deployment "%s" for "%s"', environment, revision)
-
- return gh_request('POST', url, {
- 'ref': revision,
- 'environment': environment,
- 'auto_merge': False,
- # Pull Request previews are created regardless of GitHub Commit
- # Status Checks, so Status Checks should be ignored when creating
- # GitHub Deployments.
- 'required_contexts': []
- }, 'application/vnd.github.ant-man-preview+json')
-
- @guard('core')
- def get_deployment(self, revision):
- url = '{}/repos/{}/deployments?sha={}'.format(
- self._host, self._github_project, revision
- )
-
- deployments = gh_request('GET', url)
-
- return deployments.pop() if len(deployments) else None
-
- @guard('core')
- def add_deployment_status(self, target, deployment, state, description=''):
- if state in ('pending', 'success'):
- pr_number = deployment['environment'][len(DEPLOYMENT_PREFIX):]
- environment_url = '{}/{}'.format(target, pr_number)
- else:
- environment_url = None
- url = '{}/repos/{}/deployments/{}/statuses'.format(
- self._host, self._github_project, deployment['id']
- )
-
- gh_request('POST', url, {
- 'state': state,
- 'description': description,
- 'environment_url': environment_url
- }, 'application/vnd.github.ant-man-preview+json')
-
-def is_open(pull_request):
- return not pull_request['closed_at']
-
-def has_mirroring_label(pull_request):
- for label in pull_request['labels']:
- if label['name'] == LABEL:
- return True
-
- return False
-
-def should_be_mirrored(project, pull_request):
- return (
- is_open(pull_request) and (
- has_mirroring_label(pull_request) or (
- pull_request['user']['login'] not in AUTOMATION_GITHUB_USERS and
- pull_request['author_association'] in TRUSTED_AUTHOR_ASSOCIATIONS
- )
- )
- )
-
-def is_deployed(host, deployment):
- worktree_name = deployment['environment'][len(DEPLOYMENT_PREFIX):]
- url = '{}/.git/worktrees/{}/HEAD'.format(host, worktree_name)
- logger.info('Issuing request: GET %s', url)
- response = requests.get(url)
-
- logger.info('Response status code: %s', response.status_code)
- if response.status_code != 200:
- return False
-
- logger.info('Response text: %s', response.text.strip())
- return response.text.strip() == deployment['sha']
-
-def update_mirror_refs(project, pull_request):
- '''Update the WPT refs that control mirroring of this pull request.
-
- Two sets of refs are used to control wptpr.live's mirroring of pull
- requests:
-
- 1. refs/prs-trusted-for-preview/{number}
- 2. refs/prs-open/{number}
-
- wptpr.live will only mirror a pull request if both exist for the given pull
- request number; otherwise the pull request is either not open or is not
- trustworthy (e.g. came from someone who doesn't have push access anyway.)
-
- This method returns the revision that is being mirrored, or None if the
- pull request should not be mirrored.
- '''
-
- refspec_trusted = 'prs-trusted-for-preview/{number}'.format(
- **pull_request
- )
- refspec_open = 'prs-open/{number}'.format(**pull_request)
-
- revision_latest = pull_request['head']['sha']
- revision_trusted = project.get_ref_revision(refspec_trusted)
- revision_open = project.get_ref_revision(refspec_open)
-
- if should_be_mirrored(project, pull_request):
- logger.info('Pull Request should be mirrored')
-
- if revision_trusted is None:
- project.create_ref(refspec_trusted, revision_latest)
- elif revision_trusted != revision_latest:
- project.update_ref(refspec_trusted, revision_latest)
-
- if revision_open is None:
- project.create_ref(refspec_open, revision_latest)
- elif revision_open != revision_latest:
- project.update_ref(refspec_open, revision_latest)
-
- return revision_latest
-
- logger.info('Pull Request should not be mirrored')
-
- if not has_mirroring_label(pull_request) and revision_trusted is not None:
- project.delete_ref(refspec_trusted)
-
- if revision_open is not None and not is_open(pull_request):
- project.delete_ref(refspec_open)
-
- # No revision to be deployed to wptpr.live
- return None
-
-class DeploymentFailedException(Exception):
- pass
-
-def deploy(project, target, pull_request, revision, timeout):
- '''Create a GitHub deployment for the given pull request and revision.
-
- This method creates a pending GitHub deployment, waits for the
- corresponding revision to be available on wptpr.live and marks the
- deployment as successful. If the revision does not appear in the given
- timeout, the deployment is marked as errored instead.'''
- if project.get_deployment(revision) is not None:
- return
-
- deployment = project.create_deployment(pull_request, revision)
-
- message = 'Waiting up to {} seconds for Deployment {} to be available on {}'.format(
- timeout, deployment['environment'], target
- )
- logger.info(message)
- project.add_deployment_status(target, deployment, 'pending', message)
-
- start = time.time()
- while not is_deployed(target, deployment):
- if time.time() - start > timeout:
- message = 'Deployment did not become available after {} seconds'.format(timeout)
- project.add_deployment_status(target, deployment, 'error', message)
- raise DeploymentFailedException(message)
-
- time.sleep(POLLING_PERIOD)
-
- result = project.add_deployment_status(target, deployment, 'success')
- logger.info(json.dumps(result, indent=2))
-
-def main(host, github_project, target, timeout):
- project = Project(host, github_project)
-
- with open(os.environ['GITHUB_EVENT_PATH']) as handle:
- data = json.load(handle)
-
- logger.info('Event data: %s', json.dumps(data, indent=2))
-
- pull_request = data['pull_request']
-
- logger.info('Processing Pull Request #%(number)d', pull_request)
-
- revision_to_mirror = update_mirror_refs(project, pull_request)
- if revision_to_mirror:
- deploy(project, target, pull_request, revision_to_mirror, timeout)
-
-if __name__ == '__main__':
- parser = argparse.ArgumentParser(
- description='''Mirror a pull request to an externally-hosted preview
- system, and create a GitHub Deployment associated with the pull
- request pointing at the preview.'''
- )
- parser.add_argument(
- '--host', required=True, help='the location of the GitHub API server'
- )
- parser.add_argument(
- '--github-project',
- required=True,
- help='''the GitHub organization and GitHub project name, separated by
- a forward slash (e.g. "web-platform-tests/wpt")'''
- )
- parser.add_argument(
- '--target',
- required=True,
- help='''the URL of the website to which submission previews are
- expected to become available'''
- )
- parser.add_argument(
- '--timeout',
- type=int,
- required=True,
- help='''the number of seconds to wait for a submission preview to
- become available before reporting a GitHub Deployment failure'''
- )
-
- values = dict(vars(parser.parse_args()))
- main(**values)
diff --git a/tools/ci/tests/test_pr_preview.py b/tools/ci/tests/test_pr_preview.py
deleted file mode 100644
index a268383..0000000
--- a/tools/ci/tests/test_pr_preview.py
+++ /dev/null
@@ -1,683 +0,0 @@
-try:
- from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
-except ImportError:
- # Python 3 case
- from http.server import BaseHTTPRequestHandler, HTTPServer
-import contextlib
-import errno
-import json
-import os
-import shutil
-import stat
-import subprocess
-import sys
-import tempfile
-import threading
-
-sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
-import pr_preview
-
-
-TEST_HOST = 'localhost'
-
-
-def same_members(a, b):
- if len(a) != len(b):
- return False
- a_copy = list(a)
- for elem in b:
- try:
- a_copy.remove(elem)
- except ValueError:
- return False
-
- return len(a_copy) == 0
-
-
-# When these tests are executed in Windows, files in the temporary git
-# repositories may be marked as "read only" at the moment they are intended to
-# be deleted. The following handler for `shutil.rmtree` accounts for this by
-# making the files writable and attempting to delete them a second time.
-#
-# Source:
-# https://stackoverflow.com/questions/1213706/what-user-do-python-scripts-run-as-in-windows
-def handle_remove_readonly(func, path, exc):
- excvalue = exc[1]
- candidates = (os.rmdir, os.remove, os.unlink)
- if func in candidates and excvalue.errno == errno.EACCES:
- os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) # 0777
- func(path)
- else:
- raise
-
-
-class MockHandler(BaseHTTPRequestHandler, object):
- def do_all(self):
- path = self.path.split('?')[0]
- request_body = None
-
- if 'Content-Length' in self.headers:
- request_body = self.rfile.read(
- int(self.headers['Content-Length'])
- ).decode('utf-8')
-
- if self.headers.get('Content-Type') == 'application/json':
- request_body = json.loads(request_body)
-
- for request, response in self.server.expected_traffic:
- if request[0] != self.command:
- continue
- if request[1] != path:
- continue
- body_matches = True
- for key in request[2]:
- body_matches &= request[2][key] == request_body.get(key)
- if not body_matches:
- continue
- break
- else:
- request = (self.command, path, request_body)
- response = (400, {})
-
- self.server.actual_traffic.append((request, response))
- self.send_response(response[0])
- self.end_headers()
- if self.server.reponse_body_is_json:
- self.wfile.write(json.dumps(response[1]).encode('utf-8'))
- else:
- self.wfile.write(response[1].encode('utf-8'))
-
- def do_DELETE(self):
- return self.do_all()
-
- def do_GET(self):
- return self.do_all()
-
- def do_PATCH(self):
- return self.do_all()
-
- def do_POST(self):
- return self.do_all()
-
-
-class MockServer(HTTPServer, object):
- '''HTTP server that responds to all requests with status code 200 and body
- '{}' unless an alternative status code and body are specified for the given
- method and path in the `responses` parameter.'''
- def __init__(self, address, expected_traffic, reponse_body_is_json=True):
- super(MockServer, self).__init__(address, MockHandler)
- self.expected_traffic = expected_traffic
- self.actual_traffic = []
- self.reponse_body_is_json = reponse_body_is_json
-
- def __enter__(self):
- threading.Thread(target=lambda: self.serve_forever()).start()
- return self
-
- def __exit__(self, *args):
- self.shutdown()
-
-
-class Requests(object):
- get_rate = ('GET', '/rate_limit', {})
- ref_create_open = (
- 'POST', '/repos/test-org/test-repo/git/refs', {'ref':'refs/prs-open/45'}
- )
- ref_create_trusted = (
- 'POST',
- '/repos/test-org/test-repo/git/refs',
- {'ref':'refs/prs-trusted-for-preview/45'}
- )
- ref_get_open = (
- 'GET', '/repos/test-org/test-repo/git/refs/prs-open/45', {}
- )
- ref_get_trusted = (
- 'GET', '/repos/test-org/test-repo/git/refs/prs-trusted-for-preview/45', {}
- )
- ref_update_open = (
- 'PATCH', '/repos/test-org/test-repo/git/refs/prs-open/45', {}
- )
- ref_update_trusted = (
- 'PATCH', '/repos/test-org/test-repo/git/refs/prs-trusted-for-preview/45', {}
- )
- ref_delete_open = (
- 'DELETE', '/repos/test-org/test-repo/git/refs/prs-open/45', {}
- )
- ref_delete_trusted = (
- 'DELETE', '/repos/test-org/test-repo/git/refs/prs-trusted-for-preview/45', {}
- )
- deployment_get = ('GET', '/repos/test-org/test-repo/deployments', {})
- deployment_create = ('POST', '/repos/test-org/test-repo/deployments', {})
- deployment_status_create_pending = (
- 'POST',
- '/repos/test-org/test-repo/deployments/24601/statuses',
- {'state':'pending'}
- )
- deployment_status_create_error = (
- 'POST',
- '/repos/test-org/test-repo/deployments/24601/statuses',
- {'state':'error'}
- )
- deployment_status_create_success = (
- 'POST',
- '/repos/test-org/test-repo/deployments/24601/statuses',
- {'state':'success'}
- )
- preview = ('GET', '/.git/worktrees/45/HEAD', {})
-
-
-class Responses(object):
- no_limit = (200, {
- 'resources': {
- 'search': {
- 'remaining': 100,
- 'limit': 100
- },
- 'core': {
- 'remaining': 100,
- 'limit': 100
- }
- }
- })
-
-
-@contextlib.contextmanager
-def temp_repo():
- original_dir = os.getcwd()
- directory = tempfile.mkdtemp()
- os.chdir(directory)
-
- try:
- subprocess.check_call(['git', 'init'], cwd=directory)
- # Explicitly create the default branch.
- subprocess.check_call(
- ['git', 'checkout', '-b', 'master'],
- cwd=directory
- )
- subprocess.check_call(
- ['git', 'config', 'user.name', 'example'],
- cwd=directory
- )
- subprocess.check_call(
- ['git', 'config', 'user.email', 'example@example.com'],
- cwd=directory
- )
- subprocess.check_call(
- ['git', 'commit', '--allow-empty', '-m', 'first'],
- cwd=directory
- )
-
- yield directory
- finally:
- os.chdir(original_dir)
- shutil.rmtree(
- directory, ignore_errors=False, onerror=handle_remove_readonly
- )
-
-def update_mirror_refs(pull_request, expected_traffic):
- os.environ['GITHUB_TOKEN'] = 'c0ffee'
-
- github_server = MockServer((TEST_HOST, 0), expected_traffic)
- github_port = github_server.server_address[1]
-
- method_threw = False
- with temp_repo(), github_server:
- project = pr_preview.Project(
- 'http://{}:{}'.format(TEST_HOST, github_port),
- 'test-org/test-repo',
- )
- try:
- pr_preview.update_mirror_refs(project, pull_request)
- except pr_preview.GitHubRateLimitException:
- method_threw = True
-
- return (
- method_threw,
- github_server.actual_traffic,
- )
-
-
-def deploy(pr_num, revision, expected_github_traffic, expected_preview_traffic):
- os.environ['GITHUB_TOKEN'] = 'c0ffee'
-
- github_server = MockServer((TEST_HOST, 0), expected_github_traffic)
- github_port = github_server.server_address[1]
- preview_server = MockServer((TEST_HOST, 0), expected_preview_traffic, reponse_body_is_json=False)
- preview_port = preview_server.server_address[1]
-
- method_threw = False
- with github_server, preview_server:
- project = pr_preview.Project(
- 'http://{}:{}'.format(TEST_HOST, github_port),
- 'test-org/test-repo',
- )
- target = 'http://{}:{}'.format(TEST_HOST, preview_port)
- pull_request = {'number': pr_num}
- timeout = 1
- try:
- pr_preview.deploy(project, target, pull_request, revision, timeout)
- except (pr_preview.GitHubRateLimitException, pr_preview.DeploymentFailedException):
- method_threw = True
-
- return (
- method_threw,
- github_server.actual_traffic,
- preview_server.actual_traffic
- )
-
-def test_update_mirror_refs_fail_rate_limited():
- pull_request = {
- 'number': 45,
- 'head': {'sha': 'abc123'},
- 'labels': [],
- 'user': {'login': 'stephenmcgruer'},
- 'author_association': 'COLLABORATOR',
- 'closed_at': None,
- }
- expected_traffic = [
- (Requests.get_rate, (
- 200,
- {
- 'resources': {
- 'core': {
- 'remaining': 1,
- 'limit': 10
- }
- }
- }
- ))
- ]
-
- method_threw, actual_traffic = update_mirror_refs(
- pull_request, expected_traffic
- )
-
- assert method_threw
- assert same_members(expected_traffic, actual_traffic)
-
-def test_synchronize_ignore_closed():
- # No existing refs, but a closed PR event comes in. Nothing should happen.
- pull_request = {
- 'number': 45,
- 'head': {'sha': 'abc123'},
- 'labels': [],
- 'user': {'login': 'stephenmcgruer'},
- 'author_association': 'COLLABORATOR',
- 'closed_at': '2019-10-28',
- }
- expected_traffic = [
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_get_trusted, (404, {})),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_get_open, (404, {})),
- ]
-
- method_threw, actual_traffic = update_mirror_refs(
- pull_request, expected_traffic
- )
-
- assert not method_threw
- assert same_members(expected_traffic, actual_traffic)
-
-def test_update_mirror_refs_collaborator():
- pull_request = {
- 'number': 45,
- 'head': {'sha': 'abc123'},
- 'labels': [],
- 'user': {'login': 'stephenmcgruer'},
- 'author_association': 'COLLABORATOR',
- 'closed_at': None,
- }
- expected_traffic = [
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_get_trusted, (404, {})),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_get_open, (404, {})),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_create_open, (200, {})),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_create_trusted, (200, {})),
- ]
-
- method_threw, actual_traffic, = update_mirror_refs(
- pull_request, expected_traffic
- )
-
- assert not method_threw
- assert same_members(expected_traffic, actual_traffic)
-
-def test_update_mirror_refs_ignore_collaborator_bot():
- pull_request = {
- 'number': 45,
- 'head': {'sha': 'abc123'},
- 'labels': [],
- 'user': {'login': 'chromium-wpt-export-bot'},
- 'author_association': 'COLLABORATOR',
- 'closed_at': None,
- }
- expected_traffic = [
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_get_trusted, (404, {})),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_get_open, (404, {})),
- ]
-
- method_threw, actual_traffic = update_mirror_refs(
- pull_request, expected_traffic
- )
-
- assert not method_threw
- assert same_members(expected_traffic, actual_traffic)
-
-def test_update_mirror_refs_ignore_untrusted_contributor():
- pull_request = {
- 'number': 45,
- 'head': {'sha': 'abc123'},
- 'labels': [],
- 'user': {'login': 'stephenmcgruer'},
- 'author_association': 'CONTRIBUTOR',
- 'closed_at': None,
- }
- expected_traffic = [
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_get_trusted, (404, {})),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_get_open, (404, {})),
- ]
-
- method_threw, actual_traffic = update_mirror_refs(
- pull_request, expected_traffic
- )
-
- assert not method_threw
- assert same_members(expected_traffic, actual_traffic)
-
-def test_update_mirror_refs_trusted_contributor():
- pull_request = {
- 'number': 45,
- 'head': {'sha': 'abc123'},
- # user here is a contributor (untrusted), but the issue
- # has been labelled as safe.
- 'labels': [{'name': 'safe for preview'}],
- 'user': {'login': 'Hexcles'},
- 'author_association': 'CONTRIBUTOR',
- 'closed_at': None,
- }
- expected_traffic = [
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_get_trusted, (404, {})),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_get_open, (404, {})),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_create_open, (200, {})),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_create_trusted, (200, {})),
- ]
-
- method_threw, actual_traffic = update_mirror_refs(
- pull_request, expected_traffic
- )
-
- assert not method_threw
- assert same_members(expected_traffic, actual_traffic)
-
-def test_synchronize_sync_bot_with_label():
- pull_request = {
- 'number': 45,
- 'head': {'sha': 'abc123'},
- # user here is a bot which is normally not mirrored,
- # but the issue has been labelled as safe.
- 'labels': [{'name': 'safe for preview'}],
- 'user': {'login': 'chromium-wpt-export-bot'},
- 'author_association': 'COLLABORATOR',
- 'closed_at': None,
- }
- expected_traffic = [
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_get_trusted, (404, {})),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_get_open, (404, {})),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_create_open, (200, {})),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_create_trusted, (200, {})),
- ]
-
- method_threw, actual_traffic = update_mirror_refs(
- pull_request, expected_traffic
- )
-
- assert not method_threw
- assert same_members(expected_traffic, actual_traffic)
-
-def test_update_mirror_refs_update_collaborator():
- pull_request = {
- 'number': 45,
- 'head': {'sha': 'abc123'},
- 'labels': [],
- 'user': {'login': 'stephenmcgruer'},
- 'author_association': 'COLLABORATOR',
- 'closed_at': None,
- }
- expected_traffic = [
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_get_trusted, (
- 200,
- {
- 'object': {'sha': 'def234'},
- }
- )),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_get_open, (
- 200,
- {
- 'object': {'sha': 'def234'},
- }
- )),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_update_open, (200, {})),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_update_trusted, (200, {})),
- ]
-
- method_threw, actual_traffic = update_mirror_refs(
- pull_request, expected_traffic
- )
-
- assert not method_threw
- assert same_members(expected_traffic, actual_traffic)
-
-def test_synchronize_update_member():
- pull_request = {
- 'number': 45,
- 'head': {'sha': 'abc123'},
- 'labels': [],
- 'user': {'login': 'jgraham'},
- 'author_association': 'MEMBER',
- 'closed_at': None,
- }
- expected_traffic = [
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_get_trusted, (
- 200,
- {
- 'object': {'sha': 'def234'},
- }
- )),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_get_open, (
- 200,
- {
- 'object': {'sha': 'def234'},
- }
- )),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_update_open, (200, {})),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_update_trusted, (200, {}))
- ]
-
- method_threw, actual_traffic = update_mirror_refs(
- pull_request, expected_traffic
- )
-
- assert not method_threw
- assert same_members(expected_traffic, actual_traffic)
-
-def test_update_mirror_refs_delete_collaborator():
- pull_request = {
- 'number': 45,
- 'head': {'sha': 'abc123'},
- 'labels': [],
- 'user': {'login': 'stephenmcgruer'},
- 'author_association': 'COLLABORATOR',
- 'closed_at': 2019-10-30,
- }
- expected_traffic = [
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_get_trusted, (
- 200,
- {
- 'object': {'sha': 'def234'},
- }
- )),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_get_open, (
- 200,
- {
- 'object': {'sha': 'def234'},
- }
- )),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_delete_trusted, (204, None)),
- (Requests.get_rate, Responses.no_limit),
- (Requests.ref_delete_open, (204, None)),
- ]
-
- method_threw, actual_traffic = update_mirror_refs(
- pull_request, expected_traffic
- )
-
- assert not method_threw
- assert same_members(expected_traffic, actual_traffic)
-
-def test_deploy_fail_rate_limited():
- expected_github_traffic = [
- (Requests.get_rate, (
- 200,
- {
- 'resources': {
- 'core': {
- 'remaining': 1,
- 'limit': 10
- }
- }
- }
- ))
- ]
- expected_preview_traffic = []
-
- pr_num = 45
- revision = "abcdef123"
- method_threw, actual_github_traffic, actual_preview_traffic = deploy(
- pr_num, revision, expected_github_traffic, expected_preview_traffic
- )
-
- assert method_threw
- assert actual_github_traffic == expected_github_traffic
- assert actual_preview_traffic == expected_preview_traffic
-
-def test_deploy_success():
- pr_num = 45
- revision = 'abcdef123'
-
- expected_github_traffic = [
- (Requests.get_rate, Responses.no_limit),
- (Requests.deployment_get, (200, [])),
- (Requests.get_rate, Responses.no_limit),
- (Requests.deployment_create, (200, {
- 'id': 24601,
- 'sha': revision,
- 'environment': 'wpt-preview-45',
- })),
- (Requests.get_rate, Responses.no_limit),
- (Requests.deployment_status_create_pending, (200, {})),
- (Requests.get_rate, Responses.no_limit),
- (Requests.deployment_status_create_success, (200, {}))
- ]
- expected_preview_traffic = [
- (Requests.preview, (200, revision))
- ]
-
- method_threw, actual_github_traffic, actual_preview_traffic = deploy(
- pr_num, revision, expected_github_traffic, expected_preview_traffic
- )
-
- assert not method_threw
- assert actual_github_traffic == expected_github_traffic
- assert actual_preview_traffic == expected_preview_traffic
-
-def test_deploy_timeout_missing():
- pr_num = 45
- revision = 'abcdef123'
-
- expected_github_traffic = [
- (Requests.get_rate, Responses.no_limit),
- (Requests.deployment_get, (200, [])),
- (Requests.get_rate, Responses.no_limit),
- (Requests.deployment_create, (200, {
- 'id': 24601,
- 'sha': revision,
- 'environment': 'wpt-preview-45',
- })),
- (Requests.get_rate, Responses.no_limit),
- (Requests.deployment_status_create_pending, (200, {})),
- (Requests.get_rate, Responses.no_limit),
- (Requests.deployment_status_create_error, (200, {}))
- ]
- expected_preview_traffic = [
- (Requests.preview, (404, ""))
- ]
-
- method_threw, actual_github_traffic, actual_preview_traffic = deploy(
- pr_num, revision, expected_github_traffic, expected_preview_traffic
- )
-
- assert method_threw
- assert expected_github_traffic == actual_github_traffic
- ping_count = len(actual_preview_traffic)
- assert ping_count > 0
- assert actual_preview_traffic == expected_preview_traffic * ping_count
-
-def test_deploy_timeout_wrong_revision():
- pr_num = 45
- revision = 'abcdef123'
-
- expected_github_traffic = [
- (Requests.get_rate, Responses.no_limit),
- (Requests.deployment_get, (200, [])),
- (Requests.get_rate, Responses.no_limit),
- (Requests.deployment_create, (200, {
- 'id': 24601,
- 'sha': revision,
- 'environment': 'wpt-preview-45',
- })),
- (Requests.get_rate, Responses.no_limit),
- (Requests.deployment_status_create_pending, (200, {})),
- (Requests.get_rate, Responses.no_limit),
- (Requests.deployment_status_create_error, (200, {}))
- ]
- expected_preview_traffic = [
- # wptpr.live has the wrong revision deployed
- (Requests.preview, (200, 'ghijkl456'))
- ]
-
- method_threw, actual_github_traffic, actual_preview_traffic = deploy(
- pr_num, revision, expected_github_traffic, expected_preview_traffic
- )
-
- assert method_threw
- assert expected_github_traffic == actual_github_traffic
- ping_count = len(actual_preview_traffic)
- assert ping_count > 0
- assert actual_preview_traffic == expected_preview_traffic * ping_count