From 3b26740a593defac16fa01b4ba8853aaf958541d Mon Sep 17 00:00:00 2001 From: isabelrios Date: Thu, 13 Feb 2025 17:00:59 +0100 Subject: [PATCH] Add bitrise API --- .github/workflows/staging-push.yml | 8 ++ __main__.py | 4 + bitrise.py | 59 ++++++++++++ database.py | 4 + lib/bitrise_conn.py | 148 +++++++---------------------- utils/constants.py | 3 +- 6 files changed, 109 insertions(+), 117 deletions(-) create mode 100644 bitrise.py diff --git a/.github/workflows/staging-push.yml b/.github/workflows/staging-push.yml index c9fb816..cf641fb 100644 --- a/.github/workflows/staging-push.yml +++ b/.github/workflows/staging-push.yml @@ -48,6 +48,9 @@ jobs: echo "JIRA_PASSWORD=${{ secrets.JIRA_PASSWORD }}" >> $GITHUB_ENV echo "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV echo "BUGZILLA_API_KEY=${{ secrets.BUGZILLA_API_KEY }}" >> $GITHUB_ENV + echo "BITRISE_HOST=${{ secrets.BITRISE_HOST }}" >> $GITHUB_ENV + echo "BITRISE_APP_SLUG=${{ secrets.BITRISE_APP_SLUG }}" >> $GITHUB_ENV + - name: Update DB - test runs run: python ./__main__.py --report-type test-case-coverage --platform mobile --project ALL @@ -63,9 +66,14 @@ jobs: if: always() run: python ./__main__.py --report-type bugzilla-qe-verify + - name: Bitrise Build Count + if: always() + run: python ./__main__.py --platform mobile --project firefox-ios --report-type bitrise-builds + - name: Set job log URL if: always() run: echo "JOB_LOG_URL=https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> $GITHUB_ENV + - name: Send custom JSON data to Slack workflow if: always() id: slack diff --git a/__main__.py b/__main__.py index 6e951f1..f44da25 100644 --- a/__main__.py +++ b/__main__.py @@ -1,6 +1,7 @@ import argparse import sys +from bitrise import BitriseClient from bugz import BugzillaClient from github import GithubClient from jira import JiraClient @@ -115,6 +116,9 @@ def main(): if args.report_type == 'bugzilla-qe-verify': h = BugzillaClient() h.bugzilla_qe_verify() + if args.report_type == 'bitrise-builds': + h = BitriseClient() + h.builds_count() if __name__ == '__main__': diff --git a/bitrise.py b/bitrise.py new file mode 100644 index 0000000..0e2de60 --- /dev/null +++ b/bitrise.py @@ -0,0 +1,59 @@ +import sys +import os + +from lib.bitrise_conn import BitriseAPIClient +from database import ( + Database, + ReportBitriseBuildsCount +) + + +class Bitrise: + + def __init__(self): + try: + BITRISE_HOST = os.environ['BITRISE_HOST'] + self.client = BitriseAPIClient(BITRISE_HOST) + self.BITRISE_APP_SLUG = os.environ['BITRISE_APP_SLUG'] + except KeyError: + print("ERROR: Missing bitrise env var") + sys.exit(1) + + # API: Projects + def builds(self): + return self.client.get_builds(self.BITRISE_APP_SLUG) + + +class BitriseClient(Bitrise): + + def __init__(self): + super().__init__() + print("Init") + self.db = DatabaseBitrise() + + def builds_count(self): + # Pull JSON blob from Bitrise + payload = self.builds() + + data_frame = self.db.report_bitrise_builds_count(payload) + self.db.report_bitrise_builds_count_insert(data_frame) + + +class DatabaseBitrise(Database): + + def __init__(self): + super().__init__() + self.db = Database() + + def report_bitrise_builds_count(self, payload): + # Normalize the JSON data + total_count = payload.get("paging", {}).get("total_item_count") + + data = [total_count] + return data + + def report_bitrise_builds_count_insert(self, payload): + report = ReportBitriseBuildsCount(total_builds=payload[0]) + + self.session.add(report) + self.session.commit() diff --git a/database.py b/database.py index 9e048a8..a6c08f7 100644 --- a/database.py +++ b/database.py @@ -55,6 +55,10 @@ class ReportTestRailMilestones(Base): __table__ = Table('report_testrail_milestones', Base.metadata, autoload=True) # noqa +class ReportBitriseBuildsCount(Base): + __table__ = Table('report_bitrise_builds_count', Base.metadata, autoload=True) # noqa + + class Database: def __init__(self): diff --git a/lib/bitrise_conn.py b/lib/bitrise_conn.py index 390db2c..9ba4978 100644 --- a/lib/bitrise_conn.py +++ b/lib/bitrise_conn.py @@ -1,9 +1,6 @@ -from datetime import datetime -from enum import Enum -import os -import sys +import datetime +import time -import argparse import logging import requests @@ -12,73 +9,21 @@ _logger = logging.getLogger(__name__) -def parse_args(cmdln_args): - parser = argparse.ArgumentParser( - description="Gets build data for a targeted app on Bitrise" - ) +class BitriseAPIClient: - parser.add_argument( - "--project", - help="Use selected project", - required=True, - choices=['ios', 'android'] - ) - - parser.add_argument( - "--branch", - help="Use selected branch", - required=False, - default=None - ) - - parser.add_argument( - "--workflow", - help="Use selected workflow", - required=False, - default=None - ) - - parser.add_argument( - "--before", - help="List builds run before a given date (epoch time)", - required=False, - default=None, - type=datetime.fromisoformat - ) - - parser.add_argument( - "--after", - help="List builds run after a given date (epoch time)", - required=False, - default=None, - type=datetime.fromisoformat - ) - - return parser.parse_args(args=cmdln_args) - - -class Status(Enum): - NOT_FINISHED = 0 - SUCCESSFUL = 1 - FAILURE = 2 - ABORTED_FAILURE = 3 - ABORTED_SUCCESS = 4 - - -class Bitrise: - - def __init__(self): + def __init__(self, base_url): try: - self.API_TOKEN = os.environ['BITRISE_TOKEN'] - self.API_HEADER = {'Authorization': self.API_TOKEN, - 'accept': 'application/json'} + self.API_HEADER = {'accept': 'application/json'} + self.BITRISE_APP_SLUG = '' + self.__url = base_url except KeyError: print("ERROR: must set BITRISE_TOKEN") exit() def get_apps(self): - resp = requests.get('https://api.bitrise.io/v0.1/apps', + url = self.__url + resp = requests.get(url, headers=self.API_HEADER) if resp.status_code != 200: raise print('GET /apps/ {}'.format(resp.status_code)) @@ -87,73 +32,44 @@ def get_apps(self): def set_app(self, project, apps): if apps is not None: if project == "android": - self.APP_SLUG = apps['data'][0]['slug'] + self.BITRISE_APP_SLUG = apps['data'][0]['slug'] elif project == "ios": - self.APP_SLUG = apps['data'][1]['slug'] + self.BITRISE_APP_SLUG = apps['data'][1]['slug'] def get_app(self, project, apps): - if not self.APP_SLUG: - resp = requests.get('https://api.bitrise.io/v0.1/apps/{0}'. - format(self.APP_SLUG), + url = self.__url + if not self.BITRISE_APP_SLUG: + resp = requests.get('{0}''{1}'. + format(url, self.BITRISE_APP_SLUG), headers=self.API_HEADER) if resp.status_code != 200: raise _logger.error('GET /apps/ {}'.format(resp.status_code)) return resp.json() - def get_workflows(self, APP_SLUG): + def get_workflows(self, BITRISE_APP_SLUG): + url = self.__url resp = \ - requests.get('https://api.bitrise.io/v0.1/apps/{0}' - '/build-workflows'.format(self.APP_SLUG), + requests.get('{0}{1}' + '/build-workflows'.format(url, self.BITRISE_APP_SLUG), headers=self.API_HEADER) if resp.status_code != 200: raise _logger.error('GET /apps/ {}'.format(resp.status_code)) return resp.json() - def get_builds(self, params=None): + def get_builds(self, BITRISE_APP_SLUG): + url = self.__url + days_ago = 1 + today = datetime.datetime.utcnow().date() + past_date = today - datetime.timedelta(days=days_ago) + print(past_date) + past_date_timestamp = int(time.mktime(past_date.timetuple())) + print(past_date_timestamp) + + # Change to BITRISE_HOST resp = \ - requests.get('https://api.bitrise.io/v0.1/apps/{0}' - '/builds'.format(self.APP_SLUG), - headers=self.API_HEADER, params=params) + requests.get('{0}{1}' + '/builds?after={2}'.format(url, BITRISE_APP_SLUG, past_date_timestamp), # noqa + headers=self.API_HEADER) if resp.status_code != 200: raise _logger.error('GET /apps/ {}'.format(resp.status_code)) return resp.json() - - -def main(): - args = parse_args(sys.argv[1:]) - - b = Bitrise() - - all_apps = b.get_apps() - - b.set_app(args.project, all_apps) - workflows = b.get_workflows(b.APP_SLUG) - builds = b.get_builds({"branch": args.branch, - "workflow": args.workflow, - "after": int(datetime.timestamp(args.after)) - if args.after is not None else None, - "before": int(datetime.timestamp(args.before)) - if args.before is not None else None}) - - failures = b.get_failure_count(builds) - print("All recent builds Failure count: {0}".format(failures)) - - successes = b.get_success_count(builds) - print("All recent builds Success count: {0}".format(successes)) - - not_finished = b.get_not_finished_count(builds) - print("All recent builds Not finished count: {0}".format(not_finished)) - - aborted_failure = b.get_aborted_failure(builds) - print("All recent builds Aborted Failure count: {0}" - .format(aborted_failure)) - - aborted_success = b.get_aborted_success(builds) - print("All recent builds Aborted Success count: {0}" - .format(aborted_success)) - - print(*workflows['data'], sep=", ") - - -if __name__ == '__main__': - main() diff --git a/utils/constants.py b/utils/constants.py index 87e6970..33e073b 100644 --- a/utils/constants.py +++ b/utils/constants.py @@ -31,7 +31,8 @@ 'jira-qa-requests', 'jira-qa-needed', 'bugzilla-qe-verify', - 'testrail-milestones' + 'testrail-milestones', + 'bitrise-builds' ] # JQL query All QA Requests since 2022 filter_id: 13856