commit: b83ee2fd7f2b153cff31201f18cad2aa7d048db4 Author: Thomas Bracht Laumann Jespersen <t <AT> laumann <DOT> xyz> AuthorDate: Mon Feb 9 20:21:26 2026 +0000 Commit: Michał Górny <mgorny <AT> gentoo <DOT> org> CommitDate: Tue Feb 10 08:17:12 2026 +0000 URL: https://gitweb.gentoo.org/proj/repo-mirror-ci.git/commit/?id=b83ee2fd
pull-request: consolidate github and codeberg scripts Made the PR reporting script and the status update Python script accept an additional "forge" parameter that should be either "github" or "codeberg" and pass the $forge parameter from pull-requests.bash. Remove all the codeberg-ified versions of the Python scripts, and run "ruff format" on the scripts. Signed-off-by: Thomas Bracht Laumann Jespersen <t <AT> laumann.xyz> Part-of: https://github.com/gentoo/repo-mirror-ci/pull/13 Closes: https://github.com/gentoo/repo-mirror-ci/pull/13 Signed-off-by: Michał Górny <mgorny <AT> gentoo.org> pull-request/pull-requests.bash | 46 ++---- pull-request/report-codeberg-pull-request.py | 106 ------------- pull-request/report-pull-request.py | 222 +++++++++++++++++++++------ pull-request/scan-codeberg-pull-requests.py | 117 -------------- pull-request/set-codeberg-pr-status.py | 22 --- pull-request/set-pull-request-status.py | 31 +++- 6 files changed, 212 insertions(+), 332 deletions(-) diff --git a/pull-request/pull-requests.bash b/pull-request/pull-requests.bash index 0d40d95..96d8490 100755 --- a/pull-request/pull-requests.bash +++ b/pull-request/pull-requests.bash @@ -17,20 +17,11 @@ if [[ -s ${pull}/current-pr ]]; then cd -- "${sync}" hash=$(git rev-parse "refs/pull/${pr}") case ${forge} in - github) - pr_repo="${PULL_REQUEST_REPO}" - status_script="set-pull-request-status.py" - ;; - codeberg) - pr_repo="https://codeberg.org/${CODEBERG_REPO}" - status_script="set-codeberg-pull-request-status.py" - ;; - *) - echo "unknown forge ${forge}" - exit 1 - ;; + github) pr_repo="${PULL_REQUEST_REPO}";; + codeberg) pr_repo="https://codeberg.org/${CODEBERG_REPO}";; + *) echo "unknown forge ${forge}"; exit 1;; esac - "${SCRIPT_DIR}"/pull-request/"${status_script}" "${hash}" error \ + "${SCRIPT_DIR}"/pull-request/set-pull-request-status.py "${forge}" "${hash}" error \ "QA checks crashed. Please rebase and check profile changes for syntax errors." sendmail "${CRONJOB_ADMIN_MAIL}" <<-EOF Subject: Pull request crash: ${pr} @@ -78,23 +69,14 @@ if [[ -n ${pr} ]]; then cd -- "${sync}" if ! git remote | grep -q codeberg; then - git remote add codeberg ssh://[email protected]/gentoo/gentoo + git remote add codeberg "ssh://[email protected]/${CODEBERG_REPO}" fi ref=refs/pull/${pr} case ${forge} in - github) - remote="origin" - report_script="report-pull-request.py" - ;; - codeberg) - remote="codeberg" - report_script="report-codeberg-pull-request.py" - ;; - *) - echo "unknown forge ${forge}" - exit 1 - ;; + github) remote="origin";; + codeberg) remote="codeberg";; + *) echo "unknown forge ${forge}"; exit 1;; esac git fetch -f "${remote}" "refs/pull/${prid}/head:${ref}" @@ -182,17 +164,9 @@ if [[ -n ${pr} ]]; then echo ETOOMANY > .pre-merge.borked fi fi - - case ${forge} in - github) - "${SCRIPT_DIR}"/pull-request/report-pull-request.py "${prid}" "${pr_hash}" \ - "${pull}"/gentoo-ci/borked.list .pre-merge.borked "${hash}" - ;; - codeberg) - "${SCRIPT_DIR}"/pull-request/report-codeberg-pull-request.py "${prid}" "${pr_hash}" \ + "${SCRIPT_DIR}"/pull-request/report-pull-request.py "${forge}" "${prid}" "${pr_hash}" \ "${pull}"/gentoo-ci/borked.list .pre-merge.borked "${hash}" - ;; - esac + rm -f -- "${pull}"/current-pr rm -rf -- "${pull}"/tmp "${pull}"/gentoo-ci diff --git a/pull-request/report-codeberg-pull-request.py b/pull-request/report-codeberg-pull-request.py deleted file mode 100755 index 848b89f..0000000 --- a/pull-request/report-codeberg-pull-request.py +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env python - -import datetime -import os -import os.path -import sys - -from codebergapi import CodebergAPI - - -def main(prid, prhash, borked_path, pre_borked_path, commit_hash): - CODEBERG_USERNAME = os.environ['CODEBERG_USERNAME'] - CODEBERG_TOKEN_FILE = os.environ['CODEBERG_TOKEN_FILE'] - (owner, repo) = os.environ['CODEBERG_REPO'].split('/') - - REPORT_URI_PREFIX = os.environ['GENTOO_CI_URI_PREFIX'] - - borked = [] - with open(borked_path) as f: - for l in f: - borked.append(REPORT_URI_PREFIX + '/' + prhash + '/output.html#' + l) - - pre_borked = [] - too_many_borked = False - prid = int(prid) - if borked: - with open(pre_borked_path) as f: - for l in f: - if l.strip() == 'ETOOMANY': - too_many_borked = True - break - - lf = REPORT_URI_PREFIX + '/' + prhash + '/output.html#' + l - if lf in borked: - pre_borked.append(lf) - borked.remove(lf) - - with open(CODEBERG_TOKEN_FILE) as f: - token = f.read().strip() - - with CodebergAPI(owner, repo, token) as cb: - # delete old results - had_broken = False - old_comments = [] - # note: technically we could have multiple leftover comments - for co in cb.get_reviews(prid): - if co['user']['login'] == CODEBERG_USERNAME: - body = co['body'] - if 'All QA issues have been fixed' in body: - had_broken = False - elif 'has found no issues' in body: - had_broken = False - elif 'No issues found' in body: - had_broken = False - elif 'New issues' in body: - had_broken = True - elif 'Issues already there' in body: - had_broken = True - elif 'Issues inherited from Gentoo' in body: - had_broken = True - else: - # skip comments that don't look like CI results - continue - old_comments.append(co) - - for co in old_comments: - cb.delete_review(prid, co['id']) - - report_url = REPORT_URI_PREFIX + '/' + prhash + '/output.html' - body = f'''## Pull request CI report - -*Report generated at*: {datetime.datetime.now(datetime.UTC).strftime('%Y-%m-%d %H:%M UTC')} -*Newest commit scanned*: {commit_hash} -*Status*: {':x: **broken**' if borked else ':white_check_mark: good'} -''' - - if borked or pre_borked: - if borked: - if too_many_borked: - body += '\nThere are too many broken packages to determine whether the breakages were added by the pull request. If in doubt, please rebase.\n\nIssues:' - else: - body += '\nNew issues caused by PR:\n' - for url in borked: - body += url - if pre_borked: - body += f'\nThere are existing issues already. Please look into the report to make sure none of them affect the packages in question:\n{report_url}s\n' - elif had_broken: - body += '\nAll QA issues have been fixed!\n' - else: - body += '\nNo issues found\n' - - cb.create_review(prid, body) - - if borked: - cb.commit_set_status(commit_hash, 'failure', description='PR introduced new issues', - target_url=report_url, context='gentoo-ci') - elif pre_borked: - cb.commit_set_status(commit_hash, 'success', description='No new issues found', - target_url=report_url, context='gentoo-ci') - else: - cb.commit_set_status(commit_hash, 'success', description='All pkgcheck QA checks passed', - target_url=report_url, context='gentoo-ci') - - -if __name__ == '__main__': - sys.exit(main(*sys.argv[1:])) diff --git a/pull-request/report-pull-request.py b/pull-request/report-pull-request.py index 49ed08e..776fba5 100755 --- a/pull-request/report-pull-request.py +++ b/pull-request/report-pull-request.py @@ -6,33 +6,104 @@ import os.path import sys import github +from codebergapi import CodebergAPI -def main(prid, prhash, borked_path, pre_borked_path, commit_hash): - GITHUB_USERNAME = os.environ['GITHUB_USERNAME'] - GITHUB_TOKEN_FILE = os.environ['GITHUB_TOKEN_FILE'] - GITHUB_REPO = os.environ['GITHUB_REPO'] +def report_codeberg_pr( + prid, prhash, borked, pre_borked, too_many_borked, report_uri_prefix, commit_hash +): + CODEBERG_USERNAME = os.environ["CODEBERG_USERNAME"] + CODEBERG_TOKEN_FILE = os.environ["CODEBERG_TOKEN_FILE"] + (owner, repo) = os.environ["CODEBERG_REPO"].split("/") - REPORT_URI_PREFIX = os.environ['GENTOO_CI_URI_PREFIX'] + with open(CODEBERG_TOKEN_FILE) as f: + token = f.read().strip() - borked = [] - with open(borked_path) as f: - for l in f: - borked.append(REPORT_URI_PREFIX + '/' + prhash + '/output.html#' + l) + with CodebergAPI(owner, repo, token) as cb: + # delete old results + had_broken = False + old_comments = [] + # note: technically we could have multiple leftover comments + for co in cb.get_reviews(prid): + if co["user"]["login"] == CODEBERG_USERNAME: + body = co["body"] + if "All QA issues have been fixed" in body: + had_broken = False + elif "has found no issues" in body: + had_broken = False + elif "No issues found" in body: + had_broken = False + elif "New issues" in body: + had_broken = True + elif "Issues already there" in body: + had_broken = True + elif "Issues inherited from Gentoo" in body: + had_broken = True + else: + # skip comments that don't look like CI results + continue + old_comments.append(co) + + for co in old_comments: + cb.delete_review(prid, co["id"]) + + report_url = report_uri_prefix + "/" + prhash + "/output.html" + body = f"""## Pull request CI report + +*Report generated at*: {datetime.datetime.now(datetime.UTC).strftime("%Y-%m-%d %H:%M UTC")} +*Newest commit scanned*: {commit_hash} +*Status*: {":x: **broken**" if borked else ":white_check_mark: good"} +""" + + if borked or pre_borked: + if borked: + if too_many_borked: + body += "\nThere are too many broken packages to determine whether the breakages were added by the pull request. If in doubt, please rebase.\n\nIssues:" + else: + body += "\nNew issues caused by PR:\n" + for url in borked: + body += url + if pre_borked: + body += f"\nThere are existing issues already. Please look into the report to make sure none of them affect the packages in question:\n{report_url}s\n" + elif had_broken: + body += "\nAll QA issues have been fixed!\n" + else: + body += "\nNo issues found\n" + + cb.create_review(prid, body) - pre_borked = [] - too_many_borked = False - if borked: - with open(pre_borked_path) as f: - for l in f: - if l.strip() == 'ETOOMANY': - too_many_borked = True - break - - lf = REPORT_URI_PREFIX + '/' + prhash + '/output.html#' + l - if lf in borked: - pre_borked.append(lf) - borked.remove(lf) + if borked: + cb.commit_set_status( + commit_hash, + "failure", + description="PR introduced new issues", + target_url=report_url, + context="gentoo-ci", + ) + elif pre_borked: + cb.commit_set_status( + commit_hash, + "success", + description="No new issues found", + target_url=report_url, + context="gentoo-ci", + ) + else: + cb.commit_set_status( + commit_hash, + "success", + description="All pkgcheck QA checks passed", + target_url=report_url, + context="gentoo-ci", + ) + + +def report_github_pr( + prid, prhash, borked, pre_borked, too_many_borked, report_uri_prefix, commit_hash +): + GITHUB_USERNAME = os.environ["GITHUB_USERNAME"] + GITHUB_TOKEN_FILE = os.environ["GITHUB_TOKEN_FILE"] + GITHUB_REPO = os.environ["GITHUB_REPO"] with open(GITHUB_TOKEN_FILE) as f: token = f.read().strip() @@ -48,17 +119,17 @@ def main(prid, prhash, borked_path, pre_borked_path, commit_hash): # note: technically we could have multiple leftover comments for co in pr.get_issue_comments(): if co.user.login == GITHUB_USERNAME: - if 'All QA issues have been fixed' in co.body: + if "All QA issues have been fixed" in co.body: had_broken = False - elif 'has found no issues' in co.body: + elif "has found no issues" in co.body: had_broken = False - elif 'No issues found' in co.body: + elif "No issues found" in co.body: had_broken = False - elif 'New issues' in co.body: + elif "New issues" in co.body: had_broken = True - elif 'Issues already there' in co.body: + elif "Issues already there" in co.body: had_broken = True - elif 'Issues inherited from Gentoo' in co.body: + elif "Issues inherited from Gentoo" in co.body: had_broken = True else: # skip comments that don't look like CI results @@ -67,43 +138,104 @@ def main(prid, prhash, borked_path, pre_borked_path, commit_hash): for co in old_comments: co.delete() - report_url = REPORT_URI_PREFIX + '/' + prhash + '/output.html' - body = '''## Pull request CI report + report_url = report_uri_prefix + "/" + prhash + "/output.html" + body = """## Pull request CI report *Report generated at*: %s *Newest commit scanned*: %s *Status*: %s -''' % (datetime.datetime.now(datetime.UTC).strftime('%Y-%m-%d %H:%M UTC'), - commit_hash, - ':x: **broken**' if borked else ':white_check_mark: good') +""" % ( + datetime.datetime.now(datetime.UTC).strftime("%Y-%m-%d %H:%M UTC"), + commit_hash, + ":x: **broken**" if borked else ":white_check_mark: good", + ) if borked or pre_borked: if borked: if too_many_borked: - body += '\nThere are too many broken packages to determine whether the breakages were added by the pull request. If in doubt, please rebase.\n\nIssues:' + body += "\nThere are too many broken packages to determine whether the breakages were added by the pull request. If in doubt, please rebase.\n\nIssues:" else: - body += '\nNew issues caused by PR:\n' + body += "\nNew issues caused by PR:\n" for url in borked: body += url if pre_borked: - body += '\nThere are existing issues already. Please look into the report to make sure none of them affect the packages in question:\n%s\n' % report_url + body += ( + "\nThere are existing issues already. Please look into the report to make sure none of them affect the packages in question:\n%s\n" + % report_url + ) elif had_broken: - body += '\nAll QA issues have been fixed!\n' + body += "\nAll QA issues have been fixed!\n" else: - body += '\nNo issues found\n' + body += "\nNo issues found\n" pr.create_issue_comment(body) if borked: - c.create_status('failure', description='PR introduced new issues', - target_url=report_url, context='gentoo-ci') + c.create_status( + "failure", + description="PR introduced new issues", + target_url=report_url, + context="gentoo-ci", + ) elif pre_borked: - c.create_status('success', description='No new issues found', - target_url=report_url, context='gentoo-ci') + c.create_status( + "success", + description="No new issues found", + target_url=report_url, + context="gentoo-ci", + ) else: - c.create_status('success', description='All pkgcheck QA checks passed', - target_url=report_url, context='gentoo-ci') + c.create_status( + "success", + description="All pkgcheck QA checks passed", + target_url=report_url, + context="gentoo-ci", + ) + +def main(forge, prid, prhash, borked_path, pre_borked_path, commit_hash): + REPORT_URI_PREFIX = os.environ["GENTOO_CI_URI_PREFIX"] + + borked = [] + with open(borked_path) as f: + for l in f: + borked.append(REPORT_URI_PREFIX + "/" + prhash + "/output.html#" + l) + + pre_borked = [] + too_many_borked = False + if borked: + with open(pre_borked_path) as f: + for l in f: + if l.strip() == "ETOOMANY": + too_many_borked = True + break + + lf = REPORT_URI_PREFIX + "/" + prhash + "/output.html#" + l + if lf in borked: + pre_borked.append(lf) + borked.remove(lf) -if __name__ == '__main__': + if forge == "github": + report_github_pr( + prid, + prhash, + borked, + pre_borked, + too_many_borked, + REPORT_URI_PREFIX, + commit_hash, + ) + elif forge == "codeberg": + report_codeberg_pr( + prid, + prhash, + borked, + pre_borked, + too_many_borked, + REPORT_URI_PREFIX, + commit_hash, + ) + + +if __name__ == "__main__": sys.exit(main(*sys.argv[1:])) diff --git a/pull-request/scan-codeberg-pull-requests.py b/pull-request/scan-codeberg-pull-requests.py deleted file mode 100755 index febdea9..0000000 --- a/pull-request/scan-codeberg-pull-requests.py +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/env python -# Scan open pull requests, update their statuses and print the next -# one for processing (if any). - -import errno -import os -import pickle -import sys -from datetime import datetime - -from codebergapi import CodebergAPI - - -def main(): - CODEBERG_USERNAME = os.environ['CODEBERG_USERNAME'] - CODEBERG_TOKEN_FILE = os.environ['CODEBERG_TOKEN_FILE'] - (owner, repo) = os.environ['CODEBERG_REPO'].split('/') - PULL_REQUEST_DB = os.environ['CODEBERG_PR_DB'] - - with open(CODEBERG_TOKEN_FILE) as f: - token = f.read().strip() - - db = {} - try: - with open(PULL_REQUEST_DB, 'rb') as f: - db = pickle.load(f) - except (IOError, OSError) as e: - if e.errno != errno.ENOENT: - raise - - cb = CodebergAPI(owner, repo, token) - - to_process = [] - - for pr in cb.pulls(): - # skip PRs marked noci - prnum = pr['number'] - sha = pr['head']['sha'] - if any(x['name'] == 'noci' for x in pr['labels']): - print(f'{prnum}: noci', file=sys.stderr) - - # if it made it to the cache, we probably need to wipe - # pending status - if prnum in db: - statuses = cb.commit_statuses(sha) - for status in statuses: - # skip foreign statuses - if status['creator']['login'] != CODEBERG_USERNAME: - continue - # if it's pending, mark it done - if status['status'] == 'pending': - cb.commit_set_status( - sha, - 'success', - description='Checks skipped due to [noci] label', - context='gentoo-ci', - ) - break - del db[prnum] - - continue - - # if it's not cached, get its status - if prnum not in db: - print(f'{prnum}: updating status ...', file=sys.stderr) - statuses = cb.commit_statuses(sha) - for status in statuses: - # skip foreign statuses - if status['creator']['login'] != CODEBERG_USERNAME: - continue - # if it's not pending, mark it done - if status['status'] == 'pending': - db[prnum] = '' - print(f'{prnum}: found pending', file=sys.stderr) - else: - db[prnum] = sha - print(f'{prnum}: at {sha}', file=sys.stderr) - break - else: - db[prnum] = '' - print(f'{prnum}: unprocessed', file=sys.stderr) - - if db.get(prnum, '') != sha: - to_process.append(pr) - - to_process = sorted(to_process, - key=lambda x: (not any(x['name'] == 'priority-ci' for x in pr['labels']), datetime.fromisoformat(x['updated_at']))) - for i, pr in enumerate(to_process): - prnum = pr['number'] - sha = pr['head']['sha'] - if i == 0: - desc = 'QA checks in progress...' - db[prnum] = sha - else: - desc = 'QA checks pending. Currently {}. in queue.'.format(i) - cb.commit_set_status( - sha, - 'pending', - description=desc, - context='gentoo-ci' - ) - - print(f'{prnum}: {db.get(prnum, "") or '(none)'} -> {sha}', - file=sys.stderr) - - with open(PULL_REQUEST_DB + '.tmp', 'wb') as f: - pickle.dump(db, f) - os.rename(PULL_REQUEST_DB + '.tmp', PULL_REQUEST_DB) - - if to_process: - print(to_process[0]['number']) - - return 0 - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/pull-request/set-codeberg-pr-status.py b/pull-request/set-codeberg-pr-status.py deleted file mode 100755 index 0743b07..0000000 --- a/pull-request/set-codeberg-pr-status.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -import os -import os.path -import sys - -from codebergapi import CodebergAPI - - -def main(commit_hash, stat, desc): - CODEBERG_TOKEN_FILE = os.environ['CODEBERG_TOKEN_FILE'] - (owner, repo) = os.environ['CODEBERG_REPO'].split('/') - - with open(CODEBERG_TOKEN_FILE) as f: - token = f.read().strip() - - with CodebergAPI(owner, repo, token) as cb: - cb.commit_set_status(commit_hash, stat, description=desc, context='gentoo-ci') - - -if __name__ == '__main__': - sys.exit(main(*sys.argv[1:])) diff --git a/pull-request/set-pull-request-status.py b/pull-request/set-pull-request-status.py index 9251ed4..8d61af1 100755 --- a/pull-request/set-pull-request-status.py +++ b/pull-request/set-pull-request-status.py @@ -5,12 +5,24 @@ import os.path import sys import github +from codebergapi import CodebergAPI -def main(commit_hash, stat, desc): - GITHUB_USERNAME = os.environ['GITHUB_USERNAME'] - GITHUB_TOKEN_FILE = os.environ['GITHUB_TOKEN_FILE'] - GITHUB_REPO = os.environ['GITHUB_REPO'] +def set_codeberg_pr_status(commit_hash, stat, desc): + CODEBERG_TOKEN_FILE = os.environ["CODEBERG_TOKEN_FILE"] + (owner, repo) = os.environ["CODEBERG_REPO"].split("/") + + with open(CODEBERG_TOKEN_FILE) as f: + token = f.read().strip() + + with CodebergAPI(owner, repo, token) as cb: + cb.commit_set_status(commit_hash, stat, description=desc, context="gentoo-ci") + + +def set_github_pr_status(commit_hash, stat, desc): + GITHUB_USERNAME = os.environ["GITHUB_USERNAME"] + GITHUB_TOKEN_FILE = os.environ["GITHUB_TOKEN_FILE"] + GITHUB_REPO = os.environ["GITHUB_REPO"] with open(GITHUB_TOKEN_FILE) as f: token = f.read().strip() @@ -19,8 +31,15 @@ def main(commit_hash, stat, desc): r = g.get_repo(GITHUB_REPO) c = r.get_commit(commit_hash) - c.create_status(stat, description=desc, context='gentoo-ci') + c.create_status(stat, description=desc, context="gentoo-ci") + + +def main(forge, commit_hash, stat, desc): + if forge == "github": + set_github_pr_status(commit_hash, stat, desc) + elif forge == "codeberg": + set_codeberg_pr_status(commit_hash, stat, desc) -if __name__ == '__main__': +if __name__ == "__main__": sys.exit(main(*sys.argv[1:]))
