diff --git a/contrib/buildbot/abcbot.py b/contrib/buildbot/abcbot.py --- a/contrib/buildbot/abcbot.py +++ b/contrib/buildbot/abcbot.py @@ -13,10 +13,10 @@ from logging.handlers import RotatingFileHandler +from cirrus import Cirrus from phabricator_wrapper import PhabWrapper from slackbot import SlackBot from teamcity_wrapper import TeamCity -from travis import Travis import server @@ -41,7 +41,7 @@ 'infra': 'G016CFAV8KS', } slackbot = SlackBot(slack.WebClient, slack_token, slack_channels) -travis = Travis() +cirrus = Cirrus() def main(args): @@ -59,7 +59,7 @@ tc, phab, slackbot, - travis, + cirrus, db_file_no_ext=db_file_no_ext) formater = logging.Formatter( diff --git a/contrib/buildbot/cirrus.py b/contrib/buildbot/cirrus.py new file mode 100755 --- /dev/null +++ b/contrib/buildbot/cirrus.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2021 The Bitcoin ABC developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from build import BuildStatus +import json +import requests + +BITCOIN_ABC_SECP256K1_REPO_ID = "6034374039699456" + + +class Cirrus(): + def __init__(self, base_url="https://api.cirrus-ci.com/graphql"): + self.base_url = base_url + self.logger = None + + def set_logger(self, logger): + self.logger = logger + + def get_default_branch_status(self, repo_id=BITCOIN_ABC_SECP256K1_REPO_ID): + query = """ + query {{ + repository(id: "{}") {{ + lastDefaultBranchBuild {{ + status + }} + }} + }} + """.format(repo_id) + + response = requests.post(self.base_url, json={'query': query}) + + if response.status_code != requests.codes.ok: + raise AssertionError( + "Cirrus get_default_branch_status() failed for repository {}\nResponse:\n{}".format( + repo_id, + vars(response), + ) + ) + + json_data = json.loads(response.content) + + failure_status = ['FAILED', 'ABORTED', 'ERRORED'] + success_status = ['COMPLETED'] + running_status = ['EXECUTING'] + queued_status = ['CREATED', 'TRIGGERED'] + + try: + status = json_data['data']['repository']['lastDefaultBranchBuild']['status'] or 'UNKNOWN' + except KeyError: + status = 'UNKNOWN' + + return (BuildStatus.Success if status in success_status else + BuildStatus.Failure if status in failure_status else + BuildStatus.Running if status in running_status else + BuildStatus.Queued if status in queued_status else + BuildStatus.Unknown) diff --git a/contrib/buildbot/server.py b/contrib/buildbot/server.py --- a/contrib/buildbot/server.py +++ b/contrib/buildbot/server.py @@ -47,13 +47,13 @@ logo='TeamCity' ) -BADGE_TRAVIS_BASE = RasterBadge( - label='Travis build', - logo='travis' +BADGE_CIRRUS_BASE = RasterBadge( + label='Cirrus build', + logo='cirrus-ci' ) -def create_server(tc, phab, slackbot, travis, +def create_server(tc, phab, slackbot, cirrus, db_file_no_ext=None, jsonEncoder=None): # Create Flask app for use as decorator app = Flask("abcbot") @@ -65,7 +65,7 @@ phab.setLogger(app.logger) tc.set_logger(app.logger) - travis.set_logger(app.logger) + cirrus.set_logger(app.logger) # Optionally persistable database create_server.db = { @@ -597,26 +597,28 @@ '|---|---|\n' ).format(project_name) - # secp256k1 is a special case because it has a Travis build from a + # secp256k1 is a special case because it has a Cirrus build from a # Github repo that is not managed by the build-configurations.yml config. # The status always need to be fetched. - sepc256k1_default_branch = 'master' - sepc256k1_travis_status = travis.get_branch_status( - 27431354, sepc256k1_default_branch) - travis_badge_url = BADGE_TRAVIS_BASE.get_badge_url( - message=sepc256k1_travis_status.value, - color='brightgreen' if sepc256k1_travis_status == BuildStatus.Success else 'red', + sepc256k1_cirrus_status = cirrus.get_default_branch_status() + cirrus_badge_url = BADGE_CIRRUS_BASE.get_badge_url( + message=sepc256k1_cirrus_status.value, + color=('brightgreen' if sepc256k1_cirrus_status == BuildStatus.Success else + 'red' if sepc256k1_cirrus_status == BuildStatus.Failure else + 'blue' if sepc256k1_cirrus_status == BuildStatus.Running else + 'lightblue' if sepc256k1_cirrus_status == BuildStatus.Queued else + 'inactive'), ) - # Add secp256k1 Travis to the status panel. + # Add secp256k1 Cirrus to the status panel. panel_content = add_project_header_to_panel( 'secp256k1 ([[https://github.com/Bitcoin-ABC/secp256k1 | Github]])') panel_content = add_line_to_panel( '| [[{} | {}]] | {{image uri="{}", alt="{}"}} |'.format( - 'https://travis-ci.org/github/bitcoin-abc/secp256k1', - sepc256k1_default_branch, - travis_badge_url, - sepc256k1_travis_status.value, + 'https://cirrus-ci.com/github/Bitcoin-ABC/secp256k1', + 'master', + cirrus_badge_url, + sepc256k1_cirrus_status.value, ) ) panel_content = add_line_to_panel('') diff --git a/contrib/buildbot/test/abcbot_fixture.py b/contrib/buildbot/test/abcbot_fixture.py --- a/contrib/buildbot/test/abcbot_fixture.py +++ b/contrib/buildbot/test/abcbot_fixture.py @@ -14,6 +14,7 @@ import shutil import unittest +import test.mocks.cirrus import test.mocks.fixture import test.mocks.phabricator import test.mocks.slackbot @@ -49,12 +50,12 @@ self.phab = test.mocks.phabricator.instance() self.slackbot = test.mocks.slackbot.instance() self.teamcity = test.mocks.teamcity.instance() - self.travis = test.mocks.travis.instance() + self.cirrus = test.mocks.cirrus.instance() self.app = server.create_server( self.teamcity, self.phab, self.slackbot, - self.travis, + self.cirrus, db_file_no_ext=self.db_file_no_ext, jsonEncoder=test.mocks.fixture.MockJSONEncoder).test_client() diff --git a/contrib/buildbot/test/mocks/cirrus.py b/contrib/buildbot/test/mocks/cirrus.py new file mode 100755 --- /dev/null +++ b/contrib/buildbot/test/mocks/cirrus.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2021 The Bitcoin ABC developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from cirrus import Cirrus + + +def instance(): + cirrus = Cirrus(base_url="https://test.api.cirrus-ci.com/graphql") + + return cirrus diff --git a/contrib/buildbot/test/mocks/travis.py b/contrib/buildbot/test/mocks/travis.py deleted file mode 100755 --- a/contrib/buildbot/test/mocks/travis.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2020 The Bitcoin ABC developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -import mock -import requests -from travis import Travis - - -def instance(): - travis = Travis(base_url="https://test.travis-ci.org") - travis.session = mock.Mock() - travis.session.send = mock.Mock() - travis.session.send.return_value.status_code = requests.codes.ok - - return travis diff --git a/contrib/buildbot/test/test_endpoint_status.py b/contrib/buildbot/test/test_endpoint_status.py --- a/contrib/buildbot/test/test_endpoint_status.py +++ b/contrib/buildbot/test/test_endpoint_status.py @@ -58,8 +58,8 @@ self.teamcity.getIgnoreList = mock.Mock() self.teamcity.getIgnoreList.return_value = [] - self.travis.get_branch_status = mock.Mock() - self.travis.get_branch_status.return_value = BuildStatus.Success + self.cirrus.get_default_branch_status = mock.Mock() + self.cirrus.get_default_branch_status.return_value = BuildStatus.Success def setup_master_failureAndTaskDoesNotExist(self, latestCompletedBuildId=DEFAULT_BUILD_ID, numRecentFailedBuilds=0, numCommits=1, @@ -1223,14 +1223,14 @@ self.teamcity.getBuildInfo.side_effect = _get_build_info - def get_travis_panel_content(status=None): + def get_cirrus_panel_content(status=None): if not status: status = BuildStatus.Success return ( '| secp256k1 ([[https://github.com/Bitcoin-ABC/secp256k1 | Github]]) | Status |\n' '|---|---|\n' - '| [[https://travis-ci.org/github/bitcoin-abc/secp256k1 | master]] | {{image uri="https://raster.shields.io/static/v1?label=Travis build&message={}&color={}&logo=travis", alt="{}"}} |\n\n' + '| [[https://cirrus-ci.com/github/Bitcoin-ABC/secp256k1 | master]] | {{image uri="https://raster.shields.io/static/v1?label=Cirrus build&message={}&color={}&logo=cirrus-ci", alt="{}"}} |\n\n' ).format( status.value, 'brightgreen' if status == BuildStatus.Success else 'red', @@ -1328,7 +1328,7 @@ # teamcity content set_config_file([], []) call_status('dont_care', BuildStatus.Success) - assert_panel_content(get_travis_panel_content()) + assert_panel_content(get_cirrus_panel_content()) # If branch is not master the panel is not updated self.phab.set_text_panel_content.reset_mock() @@ -1340,23 +1340,23 @@ ) self.phab.set_text_panel_content.assert_not_called() - # Turn travis build into failure - self.travis.get_branch_status.return_value = BuildStatus.Failure + # Turn cirrus build into failure + self.cirrus.get_default_branch_status.return_value = BuildStatus.Failure call_status('dont_care', BuildStatus.Success) - assert_panel_content(get_travis_panel_content(BuildStatus.Failure)) - self.travis.get_branch_status.return_value = BuildStatus.Success + assert_panel_content(get_cirrus_panel_content(BuildStatus.Failure)) + self.cirrus.get_default_branch_status.return_value = BuildStatus.Success # Some builds in config file but no associated teamcity build set_config_file(["show_me11"], []) call_status('dont_care', BuildStatus.Success) - assert_panel_content(get_travis_panel_content()) + assert_panel_content(get_cirrus_panel_content()) # Set one build to be shown and associate it. This is not the build that # just finished. associate_build("show_me11") call_status('hide_me_Type', BuildStatus.Success) assert_panel_content( - get_travis_panel_content() + + get_cirrus_panel_content() + header('Project Name') + build_line('show_me11') + @@ -1369,7 +1369,7 @@ associate_build("show_me13") call_status('hide_me_Type', BuildStatus.Success) assert_panel_content( - get_travis_panel_content() + + get_cirrus_panel_content() + header('Project Name') + build_line('show_me11') + @@ -1393,7 +1393,7 @@ for i in range(10): call_status('hide_me_Type', BuildStatus.Success) assert_panel_content( - get_travis_panel_content() + + get_cirrus_panel_content() + header('Project Name') + build_line('show_me11') + @@ -1411,7 +1411,7 @@ del associated_builds["show_me12"] call_status('hide_me_Type', BuildStatus.Success) assert_panel_content( - get_travis_panel_content() + + get_cirrus_panel_content() + header('Project Name') + build_line('show_me11') + @@ -1430,7 +1430,7 @@ del associated_builds["show_me13"] call_status('hide_me_Type', BuildStatus.Success) assert_panel_content( - get_travis_panel_content() + + get_cirrus_panel_content() + header('Project Name') + build_line('show_me11') + @@ -1447,7 +1447,7 @@ del associated_builds["show_me11"] call_status('hide_me_Type', BuildStatus.Success) assert_panel_content( - get_travis_panel_content() + + get_cirrus_panel_content() + header('Project Name 2') + build_line('show_me21') + @@ -1460,7 +1460,7 @@ failing_build_type_ids = ['show_me21_Type'] call_status('hide_me_Type', BuildStatus.Success) assert_panel_content( - get_travis_panel_content() + + get_cirrus_panel_content() + header('Project Name 2') + build_line('show_me21') + @@ -1473,7 +1473,7 @@ # and will be fetched from Teamcity anyway. call_status('show_me21_Type', BuildStatus.Success) assert_panel_content( - get_travis_panel_content() + + get_cirrus_panel_content() + header('Project Name 2') + build_line('show_me21', status=BuildStatus.Failure) + @@ -1489,7 +1489,7 @@ no_complete_build_type_ids = ['show_me23_Type'] call_status('show_me21_Type', BuildStatus.Success) assert_panel_content( - get_travis_panel_content() + + get_cirrus_panel_content() + header('Project Name 2') + build_line('show_me21', status=BuildStatus.Failure) + diff --git a/contrib/buildbot/test/test_persist_database.py b/contrib/buildbot/test/test_persist_database.py --- a/contrib/buildbot/test/test_persist_database.py +++ b/contrib/buildbot/test/test_persist_database.py @@ -44,8 +44,8 @@ json.loads(test.mocks.teamcity.buildInfo().content) ) - self.travis.get_branch_status = mock.Mock() - self.travis.get_branch_status.return_value = BuildStatus.Success + self.cirrus.get_default_branch_status = mock.Mock() + self.cirrus.get_default_branch_status.return_value = BuildStatus.Success def test_persist_diff_targets(self): queryData = buildRequestQuery() @@ -85,7 +85,7 @@ self.teamcity, self.phab, self.slackbot, - self.travis, + self.cirrus, db_file_no_ext=self.db_file_no_ext, jsonEncoder=test.mocks.fixture.MockJSONEncoder).test_client() diff --git a/contrib/buildbot/test/test_travis.py b/contrib/buildbot/test/test_travis.py deleted file mode 100755 --- a/contrib/buildbot/test/test_travis.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2020 The Bitcoin ABC developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -from build import BuildStatus -import json -import test.mocks.travis -import unittest - - -class TravisTestCase(unittest.TestCase): - def setUp(self): - self.travis = test.mocks.travis.instance() - - def tearDown(self): - pass - - def test_get_branch_status(self): - repo_id = 1234 - branch_name = 'test_branch' - - def configure_status(current, previous=None): - if not previous: - previous = 'failed' - - self.travis.session.send.return_value.content = json.dumps({ - "last_build": { - "state": current, - "previous_state": previous, - } - }) - - # No last_build data - self.travis.session.send.return_value.content = json.dumps({ - "last_build": { - } - }) - status = self.travis.get_branch_status(repo_id, branch_name) - self.assertEqual(status, BuildStatus.Failure) - - # Current status is success - configure_status('passed') - status = self.travis.get_branch_status(repo_id, branch_name) - self.assertEqual(status, BuildStatus.Success) - - # Current status is failure - configure_status('failed') - status = self.travis.get_branch_status(repo_id, branch_name) - self.assertEqual(status, BuildStatus.Failure) - - # Current status is errored - configure_status('errored') - status = self.travis.get_branch_status(repo_id, branch_name) - self.assertEqual(status, BuildStatus.Failure) - - # Current status is started, the previous status is success - configure_status('started', 'passed') - status = self.travis.get_branch_status(repo_id, branch_name) - self.assertEqual(status, BuildStatus.Success) - - # Current status is started, the previous status is failure - configure_status('started', 'failed') - status = self.travis.get_branch_status(repo_id, branch_name) - self.assertEqual(status, BuildStatus.Failure) - - # Current status is started, the previous status is unknown - configure_status('started', 'unknown') - status = self.travis.get_branch_status(repo_id, branch_name) - self.assertEqual(status, BuildStatus.Failure) - - -if __name__ == '__main__': - unittest.main() diff --git a/contrib/buildbot/travis.py b/contrib/buildbot/travis.py deleted file mode 100755 --- a/contrib/buildbot/travis.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2020 The Bitcoin ABC developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -from build import BuildStatus -import json -import requests -from urllib.parse import urljoin - - -class Travis(): - def __init__(self, base_url="https://api.travis-ci.org", api_version=3): - self.base_url = base_url - self.api_version = api_version - self.session = requests.Session() - self.logger = None - - def set_logger(self, logger): - self.logger = logger - - def get_branch_status(self, repo_id, branch_name): - endpoint = 'repo/{}/branch/{}'.format(repo_id, branch_name) - url = urljoin(self.base_url, endpoint) - - request = self.request('GET', url) - response = self.session.send(request.prepare()) - - if response.status_code != requests.codes.ok: - raise AssertionError( - "Travis get_branch_status() failed\nRequest:\n{}\nResponse:\n{}".format( - vars(request), - vars(response), - ) - ) - - data = json.loads(response.content) - - failure_status = ['failed', 'errored'] - success_status = ['passed'] - - # If the last build is not finished, use the previous status - status = data['last_build'].get('state', None) - if status not in failure_status + success_status: - status = data['last_build'].get('previous_state', None) - - return BuildStatus.Success if status in success_status else BuildStatus.Failure - - def request(self, verb, url, data=None, headers=None): - if self.logger: - self.logger.info('{}: {}'.format(verb, url)) - - if headers is None: - headers = { - 'Content-Type': 'application/json', - 'Travis-API-Version': '3', - } - - req = requests.Request( - verb, - url, - headers=headers) - req.data = data - - return req