def mark_pull_request_build_failed(pr, build_number, failure_message, config): """The given pull request failed to build. Comment on the pull request to alert the devs of this issue. """ github_info = GithubInfo( config['github.owner'], config['github.project'], config['github.username'], config['github.token'], ) jenkins_info = JenkinsInfo( config['jenkins.merge.url'], config['jenkins.merge.job'], config['jenkins.merge.token'], ) pull_request = get_pull_request(pr, github_info) build_url = generate_build_url(build_number, jenkins_info) try: comment = pull_request_build_failed(pull_request, build_url, failure_message, github_info) return comment['url'] except GithubError as exc: return 'Failed to add comment: {0}'.format(exc)
def do_merge_pull_request(pr, build_number, config): """The given pull build passed and needs to be merged. """ github_info = GithubInfo( config['github.owner'], config['github.project'], config['github.username'], config['github.token'], ) jenkins_info = JenkinsInfo( config['jenkins.merge.url'], config['jenkins.merge.job'], config['jenkins.merge.token'], ) build_url = generate_build_url(build_number, jenkins_info) try: result = merge_pull_request(pr, build_url, github_info) if result['merged']: return result['message'] else: raise GithubError('Failed to merge: {0}'.format(result['message'])) except GithubError as exc: return 'Failed to add comment: {0}'.format(exc)
def test_pull_request_build_failed(self): """Adds a comment to the pull request about the failure.""" new_comment = load_data('github-new-issue-comment.json') pulls = load_data('github-open-pulls.json', load_json=True) pull_request = pulls[0] responses.add( responses.POST, ( u'https://api.github.com/repos/CanonicalJS/juju-gui/issues/5/' u'comments' ), body=new_comment, status=201, content_type='application/json' ) info = GithubInfo('CanonicalJS', 'juju-gui', 'jujugui', '1234') result = pull_request_build_failed( pull_request, 'http://jenkins.com/job/gui/12', 'Failure message', info ) self.assertTrue('body' in result)
def test_merge_pull_request_fails(self): merge_response = load_data('github-merge-failed.json') pulls = load_data('github-open-pulls.json', load_json=True) pull_request = pulls[0] responses.add( responses.GET, 'https://api.github.com/repos/CanonicalJS/juju-gui/pulls/4', body=json.dumps(pull_request), status=200, content_type='application/json' ) responses.add( responses.PUT, 'https://api.github.com/repos/CanonicalJS/juju-gui/pulls/4/merge', body=merge_response, status=405, content_type='application/json' ) info = GithubInfo('CanonicalJS', 'juju-gui', 'jujugui', None) result = merge_pull_request( 4, 'http://jenkins.com/job/gui/12', info ) self.assertEqual(False, result['merged']) self.assertEqual("Failure reason", result['message'])
def test_merge_pull_request_fail_unplanned(self): """Still throws exception on expected request failure.""" pulls = load_data('github-open-pulls.json', load_json=True) pull_request = pulls[0] responses.add( responses.GET, 'https://api.github.com/repos/CanonicalJS/juju-gui/pulls/4', body=json.dumps(pull_request), status=200, content_type='application/json' ) responses.add( responses.PUT, 'https://api.github.com/repos/CanonicalJS/juju-gui/pulls/4/merge', body='Not Found', status=404, content_type='application/json' ) info = GithubInfo('CanonicalJS', 'juju-gui', 'jujugui', None) self.assertRaises( GithubError, merge_pull_request, 4, 'http://jenkins.com/job/gui/12', info )
def test_mergeable_pull_requests(self): pulls = load_data('github-open-pulls.json') orgs = load_data('github-user-orgs.json') comments = load_data('github-pull-request-comments.json') responses.add( responses.GET, 'https://api.github.com/users/mitechie/orgs', body=orgs, status=200, content_type='application/json' ) responses.add( responses.GET, 'https://api.github.com/repos/CanonicalJS/juju-gui/pulls', body=pulls, status=200, content_type='application/json' ) responses.add( responses.GET, ( u'https://api.github.com/repos/CanonicalJS/juju-gui/issues/5/' u'comments' ), body=comments, status=200, content_type='application/json' ) info = GithubInfo('CanonicalJS', 'juju-gui', 'jujugui', None) mergeable = mergeable_pull_requests('$$merge$$', info) self.assertEqual(1, len(mergeable)) self.assertEqual(5, mergeable[0]['number'])
def test_no_mergeable_pull_requests(self): pulls = load_data('github-open-pulls.json') responses.add( responses.GET, 'https://api.github.com/repos/juju/project/pulls', body=pulls, status=200, content_type='application/json' ) comments = load_data( 'github-pull-request-comments.json', load_json=True) # Remove the first comment since it's the trigger one. comments.pop(0) responses.add( responses.GET, ( u'https://api.github.com/repos/CanonicalJS/juju-gui/issues/5/' u'comments' ), body=json.dumps(comments), status=200, content_type='application/json' ) info = GithubInfo('juju', 'project', 'jujugui', None) mergeable = mergeable_pull_requests('$$merge$$', info) self.assertEqual(0, len(mergeable))
def test_build_url_helper_with_auth(self): """Should build a url given a path and a GithubInfo Tuple""" info = GithubInfo('juju', 'gui', 'jujugui', '1234') path = "/repos/{owner}/{project}/pulls" url = github._build_url(path, info) self.assertEqual( 'https://api.github.com/repos/juju/gui/pulls?access_token=1234', url)
def test_open_pull_requests_error(self): """Verify a non-200 throws an error""" responses.add( responses.GET, 'https://api.github.com/repos/juju/nope/pulls', body='{"error": "not found"}', status=404, content_type='application/json' ) info = GithubInfo('juju', 'nope', 'jujugui', '1234') self.assertRaises(GithubError, github.get_open_pull_requests, info)
def test_user_is_in_org(self): user_orgs = load_data('github-user-orgs.json') responses.add( responses.GET, 'https://api.github.com/users/jujugui/orgs', body=user_orgs, status=200, content_type='application/json' ) info = GithubInfo('juju', 'gui', 'jujugui', '1234') in_org = github.user_is_in_org('jujugui', 'CanonicalJS', info) self.assertTrue(in_org)
def kick_mergeable_pull_requests(config): """Check github for pull requests that include the merge command. If the merge command is found, the -merge job in jenkins is kicked to start running. :return kicked: The list of pull requests that were kicked. """ github_info = GithubInfo( config['github.owner'], config['github.project'], config['github.username'], config['github.token'], ) mergable = mergeable_pull_requests( config['jenkins.merge.trigger'], github_info, ) kicked = [] if mergable: jenkins_info = JenkinsInfo( config['jenkins.merge.url'], config['jenkins.merge.job'], config['jenkins.merge.token'], ) for pr in mergable: try: kick_jenkins_merge(pr['number'], pr['head']['sha'], jenkins_info) kicked.append('Kicking pull request: {} at sha {}'.format( pr['number'], pr['head']['sha'])) # Notify the pull request that we've scheduled a build for it. jenkins_url = generate_job_url(jenkins_info) pull_request_kicked(pr, jenkins_url, github_info) except JenkinsError as exc: kicked.append( 'Failed to kick {0}. Failure message: {1}'.format( pr['number'], exc)) return kicked
def test_pull_request_kicked(self): new_comment = load_data('github-new-issue-comment.json') pulls = load_data('github-open-pulls.json', load_json=True) pull_request = pulls[0] responses.add( responses.POST, ( u'https://api.github.com/repos/CanonicalJS/juju-gui/issues/5/' u'comments' ), body=new_comment, status=201, content_type='application/json' ) info = GithubInfo('juju', 'project', 'jujugui', None) resp = pull_request_kicked(pull_request, 'http://jenkins/job/1', info) comment = resp['body'] self.assertIn(github.MERGE_SCHEDULED, comment)
def test_not_mergable_if_already_merging(self): pulls = load_data('github-open-pulls.json') orgs = load_data('github-user-orgs.json') comments = load_data( 'github-pull-request-comments.json', load_json=True) # Add the currently merging comment to the list of the pull request to # verify it does not mark this as a mergable pull request then. merging_comment = load_data( 'github-new-issue-comment.json', load_json=True) comments.append(merging_comment) responses.add( responses.GET, 'https://api.github.com/users/mitechie/orgs', body=orgs, status=200, content_type='application/json' ) responses.add( responses.GET, 'https://api.github.com/repos/CanonicalJS/juju-gui/pulls', body=pulls, status=200, content_type='application/json' ) responses.add( responses.GET, ( u'https://api.github.com/repos/CanonicalJS/juju-gui/issues/5/' u'comments' ), body=json.dumps(comments), status=200, content_type='application/json' ) info = GithubInfo('CanonicalJS', 'juju-gui', 'jujugui', None) mergeable = mergeable_pull_requests('$$merge$$', info) self.assertEqual(0, len(mergeable))
def test_open_pull_requests(self): """Verify we can parse the list.""" resp_json = load_data('github-open-pulls.json') responses.add( responses.GET, 'https://api.github.com/repos/juju/project/pulls', body=resp_json, status=200, content_type='application/json' ) info = GithubInfo('juju', 'project', 'jujugui', None) open_requests = get_open_pull_requests(info) self.assertEqual(1, len(open_requests)) self.assertTrue( open_requests[0]['_links']['comments']['href'].endswith( '/repos/CanonicalJS/juju-gui/issues/5/comments', ) )
def test_not_mergeable_if_not_in_org(self): pulls = load_data('github-open-pulls.json') orgs = load_data('github-user-orgs.json', load_json=True) comments = load_data('github-pull-request-comments.json') # Remove the CanonicalJS group so that the user fails to be in the #org. orgs.pop(0) responses.add( responses.GET, 'https://api.github.com/users/mitechie/orgs', body=json.dumps(orgs), status=200, content_type='application/json' ) responses.add( responses.GET, 'https://api.github.com/repos/juju/project/pulls', body=pulls, status=200, content_type='application/json' ) responses.add( responses.GET, ( u'https://api.github.com/repos/CanonicalJS/juju-gui/issues/5/' u'comments' ), body=comments, status=200, content_type='application/json' ) info = GithubInfo('juju', 'project', 'jujugui', None) mergeable = mergeable_pull_requests('$$merge$$', info) self.assertEqual(0, len(mergeable))