From e58f485bd5a67fe9386abd45ed3283bbcb2cb488 Mon Sep 17 00:00:00 2001 From: driazati Date: Thu, 1 Sep 2022 13:11:59 -0700 Subject: [PATCH] Rebase --- ci/scripts/github_tvmbot.py | 22 +- tests/python/ci/sample_prs/pr10786-badci.json | 130 --- .../sample_prs/pr10786-changes-requested.json | 131 --- .../ci/sample_prs/pr10786-co-authors.json | 129 --- .../ci/sample_prs/pr10786-invalid-author.json | 130 --- .../python/ci/sample_prs/pr10786-merges.json | 129 --- .../ci/sample_prs/pr10786-missing-job.json | 129 --- .../ci/sample_prs/pr10786-nottriggered.json | 129 --- .../ci/sample_prs/pr10786-oldreview.json | 129 --- ...{pr10786-ignore-jobs.json => pr10786.json} | 5 +- .../pr11244-unauthorized-comment.json | 103 --- .../ci/sample_prs/pr11267-no-review.json | 144 ---- .../ci/sample_prs/pr11442-rerun-ci.json | 183 ---- tests/python/ci/test_ci.py | 803 ++++++++---------- tests/python/ci/test_tvmbot.py | 400 +++++---- tests/python/ci/test_utils.py | 33 +- 16 files changed, 624 insertions(+), 2105 deletions(-) delete mode 100644 tests/python/ci/sample_prs/pr10786-badci.json delete mode 100644 tests/python/ci/sample_prs/pr10786-changes-requested.json delete mode 100644 tests/python/ci/sample_prs/pr10786-co-authors.json delete mode 100644 tests/python/ci/sample_prs/pr10786-invalid-author.json delete mode 100644 tests/python/ci/sample_prs/pr10786-merges.json delete mode 100644 tests/python/ci/sample_prs/pr10786-missing-job.json delete mode 100644 tests/python/ci/sample_prs/pr10786-nottriggered.json delete mode 100644 tests/python/ci/sample_prs/pr10786-oldreview.json rename tests/python/ci/sample_prs/{pr10786-ignore-jobs.json => pr10786.json} (78%) delete mode 100644 tests/python/ci/sample_prs/pr11244-unauthorized-comment.json delete mode 100644 tests/python/ci/sample_prs/pr11267-no-review.json delete mode 100644 tests/python/ci/sample_prs/pr11442-rerun-ci.json diff --git a/ci/scripts/github_tvmbot.py b/ci/scripts/github_tvmbot.py index 3a39e69694d8..ee9607dd0254 100755 --- a/ci/scripts/github_tvmbot.py +++ b/ci/scripts/github_tvmbot.py @@ -195,6 +195,7 @@ def __init__( self.number = number self.repo_name = repo self.dry_run = dry_run + self.has_error = False if dry_run and raw_data: # In test mode there is no need to fetch anything @@ -468,7 +469,10 @@ def find_missing_expected_jobs(self) -> List[str]: def trigger_gha_ci(self, sha: str) -> None: logging.info(f"POST-ing a workflow_dispatch event to main.yml") - r = self.github.post( + actions_github = GitHubRepo( + user=self.github.user, repo=self.github.repo, token=GH_ACTIONS_TOKEN + ) + r = actions_github.post( url="actions/workflows/main.yml/dispatches", data={ "ref": "main", @@ -537,9 +541,12 @@ def rerun_github_actions(self) -> None: workflow_ids = list(set(workflow_ids)) logging.info(f"Rerunning GitHub Actions workflows with IDs: {workflow_ids}") - actions_github = GitHubRepo( - user=self.github.user, repo=self.github.repo, token=GH_ACTIONS_TOKEN - ) + if self.dry_run: + actions_github = None + else: + actions_github = GitHubRepo( + user=self.github.user, repo=self.github.repo, token=GH_ACTIONS_TOKEN + ) for workflow_id in workflow_ids: if self.dry_run: logging.info(f"Dry run, not restarting workflow {workflow_id}") @@ -576,6 +583,7 @@ def comment_failure(self, msg: str, exceptions: Union[Exception, List[Exception] comment += "" pr.comment(comment) + pr.has_error = True return exception @@ -750,6 +758,9 @@ def run(pr: PR): for name, check in command_to_run.auth: if check(pr, comment, args): logging.info(f"Passed auth check '{name}', continuing") + # Only one authorization check needs to pass (e.g. just mentionable + # or PR author), not all of them so quit + break else: logging.info(f"Failed auth check '{name}', quitting") # Add a sad face @@ -767,3 +778,6 @@ def run(pr: PR): # Run the command command_to_run.run(pr) + + if pr.has_error: + raise RuntimeError("PR commented a failure") diff --git a/tests/python/ci/sample_prs/pr10786-badci.json b/tests/python/ci/sample_prs/pr10786-badci.json deleted file mode 100644 index 7e9d10d0b648..000000000000 --- a/tests/python/ci/sample_prs/pr10786-badci.json +++ /dev/null @@ -1,130 +0,0 @@ -{ - "title": "[Hexagon] 2-d allocation cleanup", - "body": "- Added device validity check in allocation. HexagonDeviceAPI should only be called for CPU/Hexagon types.\r\n\r\n- Check for \"global.vtcm\" scope instead of \"vtcm\". The ccope of N-d allocations produced by `LowerVtcmAlloc` should be `\"global.vtcm\"`. The previous check allowed unsupported scope such as `\"local.vtcm\"`.\r\n\r\n- Remove `vtcmallocs` entry after calling free. Previously, the vtcm allocation map kept dangling pointers to `HexagonBuffer` objects after they had been freed.\r\n\r\n- Rename N-d alloc and free packed functions. Since most of the similar device functions use snake case, renaming `*.AllocND` to `*.alloc_nd` and `*.FreeND` to `*.free_nd`.\r\n\r\nCo-authored-by: Adam Straw ", - "state": "OPEN", - "author": { - "login": "abc" - }, - "comments": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [] - }, - "authorCommits": { - "nodes": [ - { - "commit": { - "authors": { - "nodes": [ - { - "name": "Eric Lunderberg", - "email": "elunderberg@octoml.ai" - }, - { - "name": "Adam Straw", - "email": "astraw@octoml.ai" - } - ] - } - } - } - ] - }, - "commits": { - "nodes": [ - { - "commit": { - "oid": "6f04bcf57d07f915a98fd91178f04d9e92a09fcd", - "statusCheckRollup": { - "contexts": { - "pageInfo": { - "hasNextPage": false - }, - "nodes": [ - { - "name": "MacOS", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945392" - }, - { - "name": "cc-reviewers", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "PR" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945029" - }, - { - "name": "tag-teams", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "Teams" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945030" - }, - { - "name": "Windows", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945524" - }, - { - "state": "FAILED", - "context": "tvm-ci/pr-head", - "targetUrl": "https://ci.tlcpack.ai/job/tvm/job/PR-10786/1/display/redirect" - } - ] - } - } - } - } - ] - }, - "reviewDecision": "APPROVED", - "reviews": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [ - { - "body": "@tvm-bot merge", - "updatedAt": "2022-03-25T22:13:50Z", - "authorCanPushToRepository": true, - "commit": { - "oid": "6f04bcf57d07f915a98fd91178f04d9e92a09fcd" - }, - "id": 123, - "author": { - "login": "kparzysz-quic" - }, - "state": "APPROVED" - } - ] - } -} \ No newline at end of file diff --git a/tests/python/ci/sample_prs/pr10786-changes-requested.json b/tests/python/ci/sample_prs/pr10786-changes-requested.json deleted file mode 100644 index 24e261099a4f..000000000000 --- a/tests/python/ci/sample_prs/pr10786-changes-requested.json +++ /dev/null @@ -1,131 +0,0 @@ -{ - "title": "[Hexagon] 2-d allocation cleanup", - "body": "- Added device validity check in allocation. HexagonDeviceAPI should only be called for CPU/Hexagon types.\r\n\r\n- Check for \"global.vtcm\" scope instead of \"vtcm\". The ccope of N-d allocations produced by `LowerVtcmAlloc` should be `\"global.vtcm\"`. The previous check allowed unsupported scope such as `\"local.vtcm\"`.\r\n\r\n- Remove `vtcmallocs` entry after calling free. Previously, the vtcm allocation map kept dangling pointers to `HexagonBuffer` objects after they had been freed.\r\n\r\n- Rename N-d alloc and free packed functions. Since most of the similar device functions use snake case, renaming `*.AllocND` to `*.alloc_nd` and `*.FreeND` to `*.free_nd`.\r\n\r\nCo-authored-by: Adam Straw ", - "state": "OPEN", - "author": { - "login": "abc" - }, - "comments": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [] - }, - "authorCommits": { - "nodes": [ - { - "commit": { - "authors": { - "nodes": [ - { - "name": "Eric Lunderberg", - "email": "elunderberg@octoml.ai" - }, - { - "name": "Adam Straw", - "email": "astraw@octoml.ai" - } - ] - } - } - } - ] - }, - "commits": { - "nodes": [ - { - "commit": { - "oid": "6f04bcf57d07f915a98fd91178f04d9e92a09fcd", - "statusCheckRollup": { - "contexts": { - "pageInfo": { - "hasNextPage": false - }, - "nodes": [ - { - "name": "MacOS", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945392" - }, - { - "name": "cc-reviewers", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "PR" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945029" - }, - { - "name": "tag-teams", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "Teams" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945030" - }, - { - "name": "Windows", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945524" - }, - { - "state": "SUCCESS", - "context": "tvm-ci/pr-head", - "targetUrl": "https://ci.tlcpack.ai/job/tvm/job/PR-10786/1/display/redirect" - } - ] - } - } - } - } - ] - }, - "reviewDecision": "CHANGES_REQUESTED", - "reviews": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [ - { - "body": "@tvm-bot merge", - "updatedAt": "2022-03-25T22:13:50Z", - "url": "https://github.com/apache/tvm/pull/10786#pullrequestreview-922186273", - "authorCanPushToRepository": true, - "commit": { - "oid": "6f04bcf57d07f915a98fd91178f04d9e92a09fcd" - }, - "id": 123, - "author": { - "login": "kparzysz-quic" - }, - "state": "CHANGES_REQUESTED" - } - ] - } -} \ No newline at end of file diff --git a/tests/python/ci/sample_prs/pr10786-co-authors.json b/tests/python/ci/sample_prs/pr10786-co-authors.json deleted file mode 100644 index 75f272825059..000000000000 --- a/tests/python/ci/sample_prs/pr10786-co-authors.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "title": "[Hexagon] 2-d allocation cleanup", - "body": "- Added device validity check in allocation. HexagonDeviceAPI should only be called for CPU/Hexagon types.\r\n\r\n- Check for \"global.vtcm\" scope instead of \"vtcm\". The ccope of N-d allocations produced by `LowerVtcmAlloc` should be `\"global.vtcm\"`. The previous check allowed unsupported scope such as `\"local.vtcm\"`.\r\n\r\n- Remove `vtcmallocs` entry after calling free. Previously, the vtcm allocation map kept dangling pointers to `HexagonBuffer` objects after they had been freed.\r\n\r\n- Rename N-d alloc and free packed functions. Since most of the similar device functions use snake case, renaming `*.AllocND` to `*.alloc_nd` and `*.FreeND` to `*.free_nd`.\r\n\r\nCo-authored-by: Adam Straw ", - "state": "OPEN", - "author": { - "login": "abc" - }, - "comments": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [] - }, - "authorCommits": { - "nodes": [ - { - "commit": { - "authors": { - "nodes": [ - { - "name": "Eric Lunderberg", - "email": "elunderberg@octoml.ai" - }, - { - "name": "Some One", - "email": "someone@email.com" - } - ] - } - } - } - ] - }, - "commits": { - "nodes": [ - { - "commit": { - "oid": "6f04bcf57d07f915a98fd91178f04d9e92a09fcd", - "statusCheckRollup": { - "contexts": { - "pageInfo": { - "hasNextPage": false - }, - "nodes": [ - { - "name": "MacOS", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945392" - }, - { - "name": "cc-reviewers", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "PR" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945029" - }, - { - "name": "tag-teams", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "Teams" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945030" - }, - { - "name": "Windows", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945524" - }, - { - "state": "SUCCESS", - "context": "tvm-ci/pr-head", - "targetUrl": "https://ci.tlcpack.ai/job/tvm/job/PR-10786/1/display/redirect" - } - ] - } - } - } - } - ] - }, - "reviewDecision": "APPROVED", - "reviews": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [ - { - "body": "@tvm-bot merge", - "updatedAt": "2022-03-25T22:13:50Z", - "authorCanPushToRepository": true, - "commit": { - "oid": "6f04bcf57d07f915a98fd91178f04d9e92a09fcd" - }, - "author": { - "login": "kparzysz-quic" - }, - "state": "APPROVED" - } - ] - } -} \ No newline at end of file diff --git a/tests/python/ci/sample_prs/pr10786-invalid-author.json b/tests/python/ci/sample_prs/pr10786-invalid-author.json deleted file mode 100644 index 81b028e3196a..000000000000 --- a/tests/python/ci/sample_prs/pr10786-invalid-author.json +++ /dev/null @@ -1,130 +0,0 @@ -{ - "title": "[Hexagon] 2-d allocation cleanup", - "body": "- Added device validity check in allocation. HexagonDeviceAPI should only be called for CPU/Hexagon types.\r\n\r\n- Check for \"global.vtcm\" scope instead of \"vtcm\". The ccope of N-d allocations produced by `LowerVtcmAlloc` should be `\"global.vtcm\"`. The previous check allowed unsupported scope such as `\"local.vtcm\"`.\r\n\r\n- Remove `vtcmallocs` entry after calling free. Previously, the vtcm allocation map kept dangling pointers to `HexagonBuffer` objects after they had been freed.\r\n\r\n- Rename N-d alloc and free packed functions. Since most of the similar device functions use snake case, renaming `*.AllocND` to `*.alloc_nd` and `*.FreeND` to `*.free_nd`.\r\n\r\nCo-authored-by: Adam Straw ", - "state": "OPEN", - "author": { - "login": "abc" - }, - "comments": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [] - }, - "authorCommits": { - "nodes": [ - { - "commit": { - "authors": { - "nodes": [ - { - "name": "Eric Lunderberg", - "email": "elunderberg@octoml.ai" - }, - { - "name": "Adam Straw", - "email": "astraw@octoml.ai" - } - ] - } - } - } - ] - }, - "commits": { - "nodes": [ - { - "commit": { - "oid": "6f04bcf57d07f915a98fd91178f04d9e92a09fcd", - "statusCheckRollup": { - "contexts": { - "pageInfo": { - "hasNextPage": false - }, - "nodes": [ - { - "name": "MacOS", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945392" - }, - { - "name": "cc-reviewers", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "PR" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945029" - }, - { - "name": "tag-teams", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "Teams" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945030" - }, - { - "name": "Windows", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945524" - }, - { - "state": "SUCCESS", - "context": "tvm-ci/pr-head", - "targetUrl": "https://ci.tlcpack.ai/job/tvm/job/PR-10786/1/display/redirect" - } - ] - } - } - } - } - ] - }, - "reviewDecision": "APPROVED", - "reviews": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [ - { - "body": "@tvm-bot merge", - "id": 123, - "updatedAt": "2022-03-25T22:13:50Z", - "authorCanPushToRepository": false, - "commit": { - "oid": "6f04bcf57d07f915a98fd91178f04d9e92a09fcd" - }, - "author": { - "login": "kparzysz-quic" - }, - "state": "APPROVED" - } - ] - } -} \ No newline at end of file diff --git a/tests/python/ci/sample_prs/pr10786-merges.json b/tests/python/ci/sample_prs/pr10786-merges.json deleted file mode 100644 index 0226c8ab5245..000000000000 --- a/tests/python/ci/sample_prs/pr10786-merges.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "title": "[Hexagon] 2-d allocation cleanup", - "body": "- Added device validity check in allocation. HexagonDeviceAPI should only be called for CPU/Hexagon types.\r\n\r\n- Check for \"global.vtcm\" scope instead of \"vtcm\". The ccope of N-d allocations produced by `LowerVtcmAlloc` should be `\"global.vtcm\"`. The previous check allowed unsupported scope such as `\"local.vtcm\"`.\r\n\r\n- Remove `vtcmallocs` entry after calling free.\n\n\nThanks for contributing to TVM! Please refer to guideline https://tvm.apache.org/docs/contribute/ for useful information and tips. After the pull request is submitted, please request code reviews from [Reviewers](https://github.com/apache/incubator-tvm/blob/master/CONTRIBUTORS.md#reviewers) by @ them in the pull request thread.\n\n\nPreviously, the vtcm allocation map kept dangling pointers to `HexagonBuffer` objects after they had been freed.\r\n\r\n- Rename N-d alloc and free packed functions. Since most of the similar device functions use snake case, renaming `*.AllocND` to `*.alloc_nd` and `*.FreeND` to `*.free_nd`.\n\n\ncc @someone\n\r\n\r\nCo-authored-by: Adam Straw \n\n\nThanks for contributing to TVM! Please refer to guideline https://tvm.apache.org/docs/contribute/ for useful information and tips. After the pull request is submitted, please request code reviews from [Reviewers](https://github.com/apache/incubator-tvm/blob/master/CONTRIBUTORS.md#reviewers) by @ them in the pull request thread.\n\n", - "state": "OPEN", - "author": { - "login": "abc" - }, - "comments": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [] - }, - "authorCommits": { - "nodes": [ - { - "commit": { - "authors": { - "nodes": [ - { - "name": "Eric Lunderberg", - "email": "elunderberg@octoml.ai" - }, - { - "name": "Adam Straw", - "email": "astraw@octoml.ai" - } - ] - } - } - } - ] - }, - "commits": { - "nodes": [ - { - "commit": { - "oid": "6f04bcf57d07f915a98fd91178f04d9e92a09fcd", - "statusCheckRollup": { - "contexts": { - "pageInfo": { - "hasNextPage": false - }, - "nodes": [ - { - "name": "MacOS", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945392" - }, - { - "name": "cc-reviewers", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "PR" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945029" - }, - { - "name": "tag-teams", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "Teams" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945030" - }, - { - "name": "Windows", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945524" - }, - { - "state": "SUCCESS", - "context": "tvm-ci/pr-head", - "targetUrl": "https://ci.tlcpack.ai/job/tvm/job/PR-10786/1/display/redirect" - } - ] - } - } - } - } - ] - }, - "reviewDecision": "APPROVED", - "reviews": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [ - { - "body": "@tvm-bot merge", - "updatedAt": "2022-03-25T22:13:50Z", - "authorCanPushToRepository": true, - "commit": { - "oid": "6f04bcf57d07f915a98fd91178f04d9e92a09fcd" - }, - "author": { - "login": "kparzysz-quic" - }, - "state": "APPROVED" - } - ] - } -} \ No newline at end of file diff --git a/tests/python/ci/sample_prs/pr10786-missing-job.json b/tests/python/ci/sample_prs/pr10786-missing-job.json deleted file mode 100644 index 13739b793fb5..000000000000 --- a/tests/python/ci/sample_prs/pr10786-missing-job.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "title": "[Hexagon] 2-d allocation cleanup", - "body": "- Added device validity check in allocation. HexagonDeviceAPI should only be called for CPU/Hexagon types.\r\n\r\n- Check for \"global.vtcm\" scope instead of \"vtcm\". The ccope of N-d allocations produced by `LowerVtcmAlloc` should be `\"global.vtcm\"`. The previous check allowed unsupported scope such as `\"local.vtcm\"`.\r\n\r\n- Remove `vtcmallocs` entry after calling free. Previously, the vtcm allocation map kept dangling pointers to `HexagonBuffer` objects after they had been freed.\r\n\r\n- Rename N-d alloc and free packed functions. Since most of the similar device functions use snake case, renaming `*.AllocND` to `*.alloc_nd` and `*.FreeND` to `*.free_nd`.\r\n\r\nCo-authored-by: Adam Straw ", - "state": "OPEN", - "author": { - "login": "abc" - }, - "comments": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [] - }, - "authorCommits": { - "nodes": [ - { - "commit": { - "authors": { - "nodes": [ - { - "name": "Eric Lunderberg", - "email": "elunderberg@octoml.ai" - }, - { - "name": "Adam Straw", - "email": "astraw@octoml.ai" - } - ] - } - } - } - ] - }, - "commits": { - "nodes": [ - { - "commit": { - "oid": "6f04bcf57d07f915a98fd91178f04d9e92a09fcd", - "statusCheckRollup": { - "contexts": { - "pageInfo": { - "hasNextPage": false - }, - "nodes": [ - { - "name": "MacOS", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945392" - }, - { - "name": "cc-reviewers", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "PR" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945029" - }, - { - "name": "tag-teams", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "Teams" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945030" - }, - { - "name": "Windows", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945524" - }, - { - "state": "SUCCESS", - "context": "tvm-ci/definitely-not-pr-head", - "targetUrl": "https://ci.tlcpack.ai/job/tvm/job/PR-10786/1/display/redirect" - } - ] - } - } - } - } - ] - }, - "reviewDecision": "APPROVED", - "reviews": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [ - { - "body": "@tvm-bot merge", - "updatedAt": "2022-03-25T22:13:50Z", - "authorCanPushToRepository": true, - "commit": { - "oid": "6f04bcf57d07f915a98fd91178f04d9e92a09fcd" - }, - "author": { - "login": "kparzysz-quic" - }, - "state": "APPROVED" - } - ] - } -} \ No newline at end of file diff --git a/tests/python/ci/sample_prs/pr10786-nottriggered.json b/tests/python/ci/sample_prs/pr10786-nottriggered.json deleted file mode 100644 index 0da541c4342d..000000000000 --- a/tests/python/ci/sample_prs/pr10786-nottriggered.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "title": "[Hexagon] 2-d allocation cleanup", - "body": "- Added device validity check in allocation. HexagonDeviceAPI should only be called for CPU/Hexagon types.\r\n\r\n- Check for \"global.vtcm\" scope instead of \"vtcm\". The ccope of N-d allocations produced by `LowerVtcmAlloc` should be `\"global.vtcm\"`. The previous check allowed unsupported scope such as `\"local.vtcm\"`.\r\n\r\n- Remove `vtcmallocs` entry after calling free. Previously, the vtcm allocation map kept dangling pointers to `HexagonBuffer` objects after they had been freed.\r\n\r\n- Rename N-d alloc and free packed functions. Since most of the similar device functions use snake case, renaming `*.AllocND` to `*.alloc_nd` and `*.FreeND` to `*.free_nd`.\r\n\r\nCo-authored-by: Adam Straw ", - "state": "OPEN", - "author": { - "login": "abc" - }, - "comments": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [] - }, - "authorCommits": { - "nodes": [ - { - "commit": { - "authors": { - "nodes": [ - { - "name": "Eric Lunderberg", - "email": "elunderberg@octoml.ai" - }, - { - "name": "Adam Straw", - "email": "astraw@octoml.ai" - } - ] - } - } - } - ] - }, - "commits": { - "nodes": [ - { - "commit": { - "oid": "6f04bcf57d07f915a98fd91178f04d9e92a09fcd", - "statusCheckRollup": { - "contexts": { - "pageInfo": { - "hasNextPage": false - }, - "nodes": [ - { - "name": "MacOS", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945392" - }, - { - "name": "cc-reviewers", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "PR" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945029" - }, - { - "name": "tag-teams", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "Teams" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945030" - }, - { - "name": "Windows", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945524" - }, - { - "state": "SUCCESS", - "context": "tvm-ci/pr-head", - "targetUrl": "https://ci.tlcpack.ai/job/tvm/job/PR-10786/1/display/redirect" - } - ] - } - } - } - } - ] - }, - "reviewDecision": "APPROVED", - "reviews": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [ - { - "body": "", - "updatedAt": "2022-03-25T22:13:50Z", - "authorCanPushToRepository": true, - "commit": { - "oid": "6f04bcf57d07f915a98fd91178f04d9e92a09fcd" - }, - "author": { - "login": "kparzysz-quic" - }, - "state": "APPROVED" - } - ] - } -} \ No newline at end of file diff --git a/tests/python/ci/sample_prs/pr10786-oldreview.json b/tests/python/ci/sample_prs/pr10786-oldreview.json deleted file mode 100644 index 1a2556cb6f5f..000000000000 --- a/tests/python/ci/sample_prs/pr10786-oldreview.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "title": "[Hexagon] 2-d allocation cleanup", - "body": "- Added device validity check in allocation. HexagonDeviceAPI should only be called for CPU/Hexagon types.\r\n\r\n- Check for \"global.vtcm\" scope instead of \"vtcm\". The ccope of N-d allocations produced by `LowerVtcmAlloc` should be `\"global.vtcm\"`. The previous check allowed unsupported scope such as `\"local.vtcm\"`.\r\n\r\n- Remove `vtcmallocs` entry after calling free. Previously, the vtcm allocation map kept dangling pointers to `HexagonBuffer` objects after they had been freed.\r\n\r\n- Rename N-d alloc and free packed functions. Since most of the similar device functions use snake case, renaming `*.AllocND` to `*.alloc_nd` and `*.FreeND` to `*.free_nd`.\r\n\r\nCo-authored-by: Adam Straw ", - "state": "OPEN", - "author": { - "login": "abc" - }, - "comments": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [] - }, - "authorCommits": { - "nodes": [ - { - "commit": { - "authors": { - "nodes": [ - { - "name": "Eric Lunderberg", - "email": "elunderberg@octoml.ai" - }, - { - "name": "Adam Straw", - "email": "astraw@octoml.ai" - } - ] - } - } - } - ] - }, - "commits": { - "nodes": [ - { - "commit": { - "oid": "6f04bcf57d07f915a98fd91178f04d9e92a09fcd", - "statusCheckRollup": { - "contexts": { - "pageInfo": { - "hasNextPage": false - }, - "nodes": [ - { - "name": "MacOS", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945392" - }, - { - "name": "cc-reviewers", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "PR" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945029" - }, - { - "name": "tag-teams", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "Teams" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945030" - }, - { - "name": "Windows", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/5694945524" - }, - { - "state": "SUCCESS", - "context": "tvm-ci/pr-head", - "targetUrl": "https://ci.tlcpack.ai/job/tvm/job/PR-10786/1/display/redirect" - } - ] - } - } - } - } - ] - }, - "reviewDecision": "APPROVED", - "reviews": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [ - { - "body": "@tvm-bot merge", - "updatedAt": "2022-03-25T22:13:50Z", - "authorCanPushToRepository": true, - "commit": { - "oid": "abc12345" - }, - "author": { - "login": "kparzysz-quic" - }, - "state": "APPROVED" - } - ] - } -} \ No newline at end of file diff --git a/tests/python/ci/sample_prs/pr10786-ignore-jobs.json b/tests/python/ci/sample_prs/pr10786.json similarity index 78% rename from tests/python/ci/sample_prs/pr10786-ignore-jobs.json rename to tests/python/ci/sample_prs/pr10786.json index dfcd806ff14b..79f20ca6094b 100644 --- a/tests/python/ci/sample_prs/pr10786-ignore-jobs.json +++ b/tests/python/ci/sample_prs/pr10786.json @@ -1,6 +1,6 @@ { "title": "[Hexagon] 2-d allocation cleanup", - "body": "- Added device validity check in allocation. HexagonDeviceAPI should only be called for CPU/Hexagon types.\r\n\r\n- Check for \"global.vtcm\" scope instead of \"vtcm\". The ccope of N-d allocations produced by `LowerVtcmAlloc` should be `\"global.vtcm\"`. The previous check allowed unsupported scope such as `\"local.vtcm\"`.\r\n\r\n- Remove `vtcmallocs` entry after calling free. Previously, the vtcm allocation map kept dangling pointers to `HexagonBuffer` objects after they had been freed.\r\n\r\n- Rename N-d alloc and free packed functions. Since most of the similar device functions use snake case, renaming `*.AllocND` to `*.alloc_nd` and `*.FreeND` to `*.free_nd`.\r\n\r\nCo-authored-by: Adam Straw ", + "body": "- Added device validity check in allocation. HexagonDeviceAPI should only be called for CPU/Hexagon types.\r\n\r\n- Check for \"global.vtcm\" scope instead of \"vtcm\". The ccope of N-d allocations produced by `LowerVtcmAlloc` should be `\"global.vtcm\"`. The previous check allowed unsupported scope such as `\"local.vtcm\"`.\r\n\r\n- Remove `vtcmallocs` entry after calling free.\n\n\nThanks for contributing to TVM! Please refer to guideline https://tvm.apache.org/docs/contribute/ for useful information and tips. After the pull request is submitted, please request code reviews from [Reviewers](https://github.com/apache/incubator-tvm/blob/master/CONTRIBUTORS.md#reviewers) by @ them in the pull request thread.\n\n\nPreviously, the vtcm allocation map kept dangling pointers to `HexagonBuffer` objects after they had been freed.\r\n\r\n- Rename N-d alloc and free packed functions. Since most of the similar device functions use snake case, renaming `*.AllocND` to `*.alloc_nd` and `*.FreeND` to `*.free_nd`.\n\n\ncc @someone\n\r\n\r\nCo-authored-by: Adam Straw \n\n\nThanks for contributing to TVM! Please refer to guideline https://tvm.apache.org/docs/contribute/ for useful information and tips. After the pull request is submitted, please request code reviews from [Reviewers](https://github.com/apache/incubator-tvm/blob/master/CONTRIBUTORS.md#reviewers) by @ them in the pull request thread.\n\n", "state": "OPEN", "author": { "login": "abc" @@ -65,7 +65,7 @@ } }, "status": "COMPLETED", - "conclusion": "FAILED", + "conclusion": "SUCCESS", "url": "https://github.com/apache/tvm/runs/5694945029" }, { @@ -119,7 +119,6 @@ "commit": { "oid": "6f04bcf57d07f915a98fd91178f04d9e92a09fcd" }, - "id": 123, "author": { "login": "kparzysz-quic" }, diff --git a/tests/python/ci/sample_prs/pr11244-unauthorized-comment.json b/tests/python/ci/sample_prs/pr11244-unauthorized-comment.json deleted file mode 100644 index beafc05958b6..000000000000 --- a/tests/python/ci/sample_prs/pr11244-unauthorized-comment.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "title": "[CRT runtime] Added functions TVMPlatformPreFuncCall and TVMPlatformPostFuncCall", - "body": "See [this thread ](https://discuss.tvm.apache.org/t/crt-add-platform-specific-pre-and-post-function-calls-in-crt-runtime/12723)for an explanation.", - "state": "OPEN", - "author": { - "login": "abc" - }, - "comments": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [ - { - "authorAssociation": "NONE", - "author": { - "login": "abc" - }, - "updatedAt": "2022-05-09T13:39:04Z", - "body": "@tvm-bot merge" - }, - { - "authorAssociation": "CONTRIBUTOR", - "author": { - "login": "areusch" - }, - "updatedAt": "2022-05-11T19:22:01Z", - "body": "i commented on the discuss forum thread. let's resolve there and then continue this PR." - } - ] - }, - "authorCommits": { - "nodes": [ - { - "commit": { - "authors": { - "nodes": [ - { - "name": "Federico Peccia", - "email": "peccia@fzi.de" - } - ] - } - } - } - ] - }, - "commits": { - "nodes": [ - { - "commit": { - "oid": "79d355c5f837b3bdadb5d25b2a5d0d2802783ae2", - "statusCheckRollup": { - "contexts": { - "pageInfo": { - "hasNextPage": false - }, - "nodes": [ - { - "name": "cc-reviewers", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "PR" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/6352791017" - }, - { - "name": "tag-teams", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "Teams" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/6352791014" - }, - { - "state": "ERROR", - "context": "tvm-ci/pr-head", - "targetUrl": "https://ci.tlcpack.ai/job/tvm/job/PR-11244/1/display/redirect" - } - ] - } - } - } - } - ] - }, - "reviewDecision": "REVIEW_REQUIRED", - "reviews": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [] - } -} \ No newline at end of file diff --git a/tests/python/ci/sample_prs/pr11267-no-review.json b/tests/python/ci/sample_prs/pr11267-no-review.json deleted file mode 100644 index d2ad164673e5..000000000000 --- a/tests/python/ci/sample_prs/pr11267-no-review.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "title": "[ci][docker] Use sccache everywhere by default", - "body": "This adds `/opt/sccache` to the PATH of each of the CI docker images so when cmake looks for a C compiler it will pick up the sccache wrapper by default. This fixes some issues where compiler invocations weren't being run though sccache. With this approach the invoker doesn't need to do anything specific to set up sccache.\n\nThis will require a follow up PR to update the Docker images and remove some of the sccache logic in `task_build.py`\n\n\n\ncc @Mousius @areusch", - "state": "OPEN", - "author": { - "login": "abc" - }, - "comments": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [ - { - "authorAssociation": "CONTRIBUTOR", - "author": { - "login": "areusch" - }, - "id": 124, - "updatedAt": "2022-05-11T16:54:32Z", - "body": "just confirming--we can disable this when doing a local build, correct? what's the mechanism by which we do that?" - }, - { - "authorAssociation": "COLLABORATOR", - "author": { - "login": "driazati" - }, - "id": 123, - "updatedAt": "2022-05-11T18:46:54Z", - "body": "@tvm-bot merge" - } - ] - }, - "authorCommits": { - "nodes": [ - { - "commit": { - "authors": { - "nodes": [ - { - "name": "driazati", - "email": "driazati@users.noreply.github.com" - } - ] - } - } - } - ] - }, - "commits": { - "nodes": [ - { - "commit": { - "oid": "bb7f51d3e0fd50997012dfcce3c9b2b852cd3136", - "statusCheckRollup": { - "contexts": { - "pageInfo": { - "hasNextPage": false - }, - "nodes": [ - { - "name": "MacOS", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/6377784092" - }, - { - "name": "cc-reviewers", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "PR" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/6377778488" - }, - { - "name": "cc-reviewers", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "PR" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/6390508806" - }, - { - "name": "tag-teams", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "Teams" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/6390511833" - }, - { - "name": "Windows", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/6377784248" - }, - { - "state": "SUCCESS", - "context": "tvm-ci/pr-head", - "targetUrl": "https://ci.tlcpack.ai/job/tvm/job/PR-11267/2/display/redirect" - } - ] - } - } - } - } - ] - }, - "reviewDecision": "REVIEW_REQUIRED", - "reviews": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [] - } -} \ No newline at end of file diff --git a/tests/python/ci/sample_prs/pr11442-rerun-ci.json b/tests/python/ci/sample_prs/pr11442-rerun-ci.json deleted file mode 100644 index 0199b2921f64..000000000000 --- a/tests/python/ci/sample_prs/pr11442-rerun-ci.json +++ /dev/null @@ -1,183 +0,0 @@ -{ - "title": "Add 'static_library' runtime::Module", - "body": "(See https://discuss.tvm.apache.org/t/byoc-supporting-cutlass-byoc-with-collage/12796/6 for\r\ncontext, which in turn is part of Collage (https://github.com/apache/tvm-rfcs/blob/main/rfcs/0062-collage.md).\r\n\r\nThis adds a new 'DSO exportable' runtime module representing the contents of a .o file. It\r\nallows external codegen toolchains to yield a result which:\r\n - Like CSource modules, can be conveyed directly to the final export_library compilation\r\n step for linking into the final .so and saved to a know location without risk the\r\n underlying code artifact will be lost.\r\n - Like DSOLibrary modules, are self contained so that no additional compile-time arguments\r\n need be conveyed from the CSource module to the final export_library command line\r\n\r\nSince this is the third flavor of 'DSO exportable' module, add a Module::IsDSOExportable.\r\n\r\nSince adding the above, can't resist also adding a Module::ImplementsFunction virtual and\r\ncalling it from TEComplier to check if an external codegen function actually provided the\r\nimplementation it promised.\r\n\r\nNote:\r\n - I've left the existing implementation of runtime.load_module alone which\r\n relinks .o files to .so files.\r\n - Though also contained in the .o metadata, I require static libraries to always\r\n carry their list of exported function names.\r\n\r\nThis is all pretty stop gap pending a good rework of TVM to supoprt the notion of artifacts\r\nand, perhaps, build rules.\r\n", - "state": "OPEN", - "author": { - "login": "abc" - }, - "comments": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [ - { - "authorAssociation": "MEMBER", - "author": { - "login": "tqchen" - }, - "updatedAt": "2022-05-24T22:13:29Z", - "body": "Thanks @mbs-octoml . I think we go with this as a temp workaround with a mind that the IsDSOExportable and ImplementsFunction likely should go to Artifact." - }, - { - "authorAssociation": "CONTRIBUTOR", - "author": { - "login": "mbs-octoml" - }, - "updatedAt": "2022-05-24T22:56:07Z", - "body": "Yeah, we really need to put some love into that.\r\n\r\nCollecting all the pieces needed for deployment along with their metadata a la Artifact is pretty clearly needed, though I suspect that will need to be abstract to cover the spectrum from firmware image to dynamically loadable .so to ready-to-call JITed code to tar.\r\n\r\nI can't help thinking we should also think about build rules guarded by target kinds & attributes, since again there's just so may ways to proceed." - }, - { - "authorAssociation": "MEMBER", - "author": { - "login": "tqchen" - }, - "updatedAt": "2022-05-24T23:08:00Z", - "body": "Perhaps we will end up building our own cmake/bazel :p in another time" - }, - { - "authorAssociation": "CONTRIBUTOR", - "author": { - "login": "mbs-octoml" - }, - "updatedAt": "2022-05-25T22:11:44Z", - "body": "Thanks Tianqi. Let's see if this new fancy bot works...\r\n\r\n" - }, - { - "authorAssociation": "CONTRIBUTOR", - "author": { - "login": "mbs-octoml" - }, - "updatedAt": "2022-05-25T22:11:50Z", - "body": "@tvm-bot merge" - }, - { - "authorAssociation": "NONE", - "author": { - "login": "github-actions" - }, - "updatedAt": "2022-05-25T22:12:10Z", - "body": "Cannot merge, did not find any approving reviews from users with write access on 96d4e62da5a7b78da18d0ee28cc6261d8fbf31c4" - }, - { - "authorAssociation": "CONTRIBUTOR", - "author": { - "login": "mbs-octoml" - }, - "updatedAt": "2022-05-25T22:12:37Z", - "body": "@tvm-bot rerun" - } - ] - }, - "authorCommits": { - "nodes": [ - { - "commit": { - "authors": { - "nodes": [ - { - "name": "mbs-octoml", - "email": "mbs@octoml.ai" - } - ] - } - } - } - ] - }, - "commits": { - "nodes": [ - { - "commit": { - "oid": "96d4e62da5a7b78da18d0ee28cc6261d8fbf31c4", - "statusCheckRollup": { - "contexts": { - "pageInfo": { - "hasNextPage": false - }, - "nodes": [ - { - "name": "MacOS", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/6598275844" - }, - { - "name": "cc-reviewers", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "PR" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/6598273162" - }, - { - "name": "Windows", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/6598275717" - }, - { - "name": "Android", - "checkSuite": { - "workflowRun": { - "workflow": { - "name": "CI" - } - } - }, - "status": "COMPLETED", - "conclusion": "SUCCESS", - "url": "https://github.com/apache/tvm/runs/6598275593" - }, - { - "state": "SUCCESS", - "context": "tvm-ci/pr-head", - "targetUrl": "https://ci.tlcpack.ai/job/tvm/job/PR-11442/4/display/redirect" - } - ] - } - } - } - } - ] - }, - "reviewDecision": "APPROVED", - "reviews": { - "pageInfo": { - "hasPreviousPage": false - }, - "nodes": [ - { - "body": "", - "updatedAt": "2022-05-24T23:08:31Z", - "url": "https://github.com/apache/tvm/pull/11442#pullrequestreview-983954561", - "authorCanPushToRepository": true, - "commit": { - "oid": "23c600097cf1c2a55acda059626a060e106dd023" - }, - "author": { - "login": "tqchen" - }, - "state": "APPROVED" - } - ] - } -} \ No newline at end of file diff --git a/tests/python/ci/test_ci.py b/tests/python/ci/test_ci.py index 0939aae10ab5..f2e686d1e582 100644 --- a/tests/python/ci/test_ci.py +++ b/tests/python/ci/test_ci.py @@ -23,15 +23,14 @@ import pytest import tvm.testing -from .test_utils import REPO_ROOT, TempGit +from .test_utils import REPO_ROOT, TempGit, run_script -def parameterize_named(*values): - keys = list(values[0].keys()) - if len(keys) == 1: - return pytest.mark.parametrize(",".join(keys), [d[keys[0]] for d in values]) - - return pytest.mark.parametrize(",".join(keys), [tuple(d.values()) for d in values]) +def parameterize_named(**kwargs): + keys = next(iter(kwargs.values())).keys() + return pytest.mark.parametrize( + ",".join(keys), [tuple(d.values()) for d in kwargs.values()], ids=kwargs.keys() + ) # pylint: disable=line-too-long @@ -137,23 +136,7 @@ def parameterize_named(*values): @tvm.testing.skip_if_wheel_test -@pytest.mark.parametrize( - [ - "main_xml_file", - "main_xml_content", - "pr_xml_file", - "pr_xml_content", - "target_url", - "s3_prefix", - "jenkins_prefix", - "common_main_build", - "commit_sha", - "expected_url", - "expected_body", - ], - [tuple(d.values()) for d in TEST_DATA_SKIPPED_BOT.values()], - ids=TEST_DATA_SKIPPED_BOT.keys(), -) +@parameterize_named(**TEST_DATA_SKIPPED_BOT) # pylint: enable=line-too-long def test_skipped_tests_comment( tmpdir_factory, @@ -182,49 +165,37 @@ def write_xml_file(root_dir, xml_file, xml_content): f.write(textwrap.dedent(xml_content)) git = TempGit(tmpdir_factory.mktemp("tmp_git_dir")) - git.run("init") - git.run("checkout", "-b", "main") - git.run("remote", "add", "origin", "https://github.com/apache/tvm.git") pr_test_report_dir = Path(git.cwd) / "pr-reports" write_xml_file(pr_test_report_dir, pr_xml_file, pr_xml_content) main_test_report_dir = Path(git.cwd) / "main-reports" write_xml_file(main_test_report_dir, main_xml_file, main_xml_content) - proc = subprocess.run( + proc = run_script( [ - str(skipped_tests_script), + skipped_tests_script, "--dry-run", f"--s3-prefix={s3_prefix}", f"--jenkins-prefix={jenkins_prefix}", f"--common-main-build={common_main_build}", ], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, env={"TARGET_URL": target_url, "COMMIT_SHA": commit_sha}, - encoding="utf-8", cwd=git.cwd, - check=False, ) - if proc.returncode != 0: - raise RuntimeError(f"Process failed:\nstdout:\n{proc.stdout}\n\nstderr:\n{proc.stderr}") - assert f"Dry run, would have posted {expected_url} with data {expected_body}." in proc.stderr + assert_in(f"Dry run, would have posted {expected_url} with data {expected_body}.", proc.stderr) @tvm.testing.skip_if_wheel_test -@pytest.mark.parametrize( - "target_url,base_url,commit_sha,expected_url,expected_body", - [ - ( - "https://ci.tlcpack.ai/job/tvm/job/PR-11594/3/display/redirect", - "https://pr-docs.tlcpack.ai", - "SHA", - "issues/11594/comments", - "\n\nBuilt docs for commit SHA can be found " - "[here](https://pr-docs.tlcpack.ai/PR-11594/3/docs/index.html).", - ) - ], +@parameterize_named( + doc_link=dict( + target_url="https://ci.tlcpack.ai/job/tvm/job/PR-11594/3/display/redirect", + base_url="https://pr-docs.tlcpack.ai", + commit_sha="SHA", + expected_url="issues/11594/comments", + expected_body="\n\nBuilt docs for commit SHA can be found " + "[here](https://pr-docs.tlcpack.ai/PR-11594/3/docs/index.html).", + ) ) def test_docs_comment( tmpdir_factory, target_url, base_url, commit_sha, expected_url, expected_body @@ -235,146 +206,93 @@ def test_docs_comment( docs_comment_script = REPO_ROOT / "ci" / "scripts" / "github_docs_comment.py" git = TempGit(tmpdir_factory.mktemp("tmp_git_dir")) - git.run("init") - git.run("checkout", "-b", "main") - git.run("remote", "add", "origin", "https://github.com/apache/tvm.git") - proc = subprocess.run( - [str(docs_comment_script), "--dry-run", f"--base-url-docs={base_url}"], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + proc = run_script( + [docs_comment_script, "--dry-run", f"--base-url-docs={base_url}"], env={"TARGET_URL": target_url, "COMMIT_SHA": commit_sha}, - encoding="utf-8", cwd=git.cwd, - check=False, ) - if proc.returncode != 0: - raise RuntimeError(f"Process failed:\nstdout:\n{proc.stdout}\n\nstderr:\n{proc.stderr}") - assert f"Dry run, would have posted {expected_url} with data {expected_body}." in proc.stderr + assert_in(f"Dry run, would have posted {expected_url} with data {expected_body}.", proc.stderr) @tvm.testing.skip_if_wheel_test -def test_cc_reviewers(tmpdir_factory): - """ - Test that reviewers are added from 'cc @someone' messages in PRs - """ - reviewers_script = REPO_ROOT / "ci" / "scripts" / "github_cc_reviewers.py" - - def run(pr_body, requested_reviewers, existing_review_users, expected_reviewers): - git = TempGit(tmpdir_factory.mktemp("tmp_git_dir")) - git.run("init") - git.run("checkout", "-b", "main") - git.run("remote", "add", "origin", "https://github.com/apache/tvm.git") - reviews = [{"user": {"login": r}} for r in existing_review_users] - requested_reviewers = [{"login": r} for r in requested_reviewers] - proc = subprocess.run( - [str(reviewers_script), "--dry-run", "--testing-reviews-json", json.dumps(reviews)], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env={ - "PR": json.dumps( - {"number": 1, "body": pr_body, "requested_reviewers": requested_reviewers} - ) - }, - encoding="utf-8", - cwd=git.cwd, - check=False, - ) - if proc.returncode != 0: - raise RuntimeError(f"Process failed:\nstdout:\n{proc.stdout}\n\nstderr:\n{proc.stderr}") - - assert f"After filtering existing reviewers, adding: {expected_reviewers}" in proc.stdout - - run(pr_body="abc", requested_reviewers=[], existing_review_users=[], expected_reviewers=[]) - run( +@parameterize_named( + cc_no_one=dict( + pr_body="abc", requested_reviewers=[], existing_review_users=[], expected_reviewers=[] + ), + cc_abc=dict( pr_body="cc @abc", requested_reviewers=[], existing_review_users=[], expected_reviewers=["abc"], - ) - run(pr_body="cc @", requested_reviewers=[], existing_review_users=[], expected_reviewers=[]) - run( + ), + bad_cc_line=dict( + pr_body="cc @", requested_reviewers=[], existing_review_users=[], expected_reviewers=[] + ), + cc_multiple=dict( pr_body="cc @abc @def", requested_reviewers=[], existing_review_users=[], expected_reviewers=["abc", "def"], - ) - run( + ), + with_existing=dict( pr_body="some text cc @abc @def something else", requested_reviewers=[], existing_review_users=[], expected_reviewers=["abc", "def"], - ) - run( + ), + with_existing_split=dict( pr_body="some text cc @abc @def something else\n\n another cc @zzz z", requested_reviewers=[], existing_review_users=[], expected_reviewers=["abc", "def", "zzz"], - ) - run( + ), + with_existing_request=dict( pr_body="some text cc @abc @def something else\n\n another cc @zzz z", requested_reviewers=["abc"], existing_review_users=[], expected_reviewers=["def", "zzz"], - ) - run( + ), + with_existing_reviewers=dict( pr_body="some text cc @abc @def something else\n\n another cc @zzz z", requested_reviewers=["abc"], existing_review_users=["abc"], expected_reviewers=["def", "zzz"], - ) - run( + ), + with_no_reviewers=dict( pr_body="some text cc @abc @def something else\n\n another cc @zzz z", requested_reviewers=[], existing_review_users=["abc"], expected_reviewers=["def", "zzz"], - ) - - -def test_update_branch(tmpdir_factory): + ), +) +def test_cc_reviewers( + tmpdir_factory, pr_body, requested_reviewers, existing_review_users, expected_reviewers +): """ - Test that the last-successful branch script updates successfully + Test that reviewers are added from 'cc @someone' messages in PRs """ - update_script = REPO_ROOT / "ci" / "scripts" / "update_branch.py" - - def run(statuses, expected_rc, expected_output): - git = TempGit(tmpdir_factory.mktemp("tmp_git_dir")) - git.run("init") - git.run("checkout", "-b", "main") - git.run("remote", "add", "origin", "https://github.com/apache/tvm.git") - commit = { - "statusCheckRollup": {"contexts": {"nodes": statuses}}, - "oid": "123", - "messageHeadline": "hello", - } - data = { - "data": { - "repository": { - "defaultBranchRef": {"target": {"history": {"edges": [], "nodes": [commit]}}} - } - } - } - proc = subprocess.run( - [str(update_script), "--dry-run", "--testonly-json", json.dumps(data)], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - encoding="utf-8", - cwd=git.cwd, - check=False, - ) + reviewers_script = REPO_ROOT / "ci" / "scripts" / "github_cc_reviewers.py" - if proc.returncode != expected_rc: - raise RuntimeError( - f"Wrong return code:\nstdout:\n{proc.stdout}\n\nstderr:\n{proc.stderr}" + git = TempGit(tmpdir_factory.mktemp("tmp_git_dir")) + reviews = [{"user": {"login": r}} for r in existing_review_users] + requested_reviewers = [{"login": r} for r in requested_reviewers] + proc = run_script( + [reviewers_script, "--dry-run", "--testing-reviews-json", json.dumps(reviews)], + env={ + "PR": json.dumps( + {"number": 1, "body": pr_body, "requested_reviewers": requested_reviewers} ) + }, + cwd=git.cwd, + ) - if expected_output not in proc.stdout: - raise RuntimeError( - f"Missing {expected_output}:\nstdout:\n{proc.stdout}\n\nstderr:\n{proc.stderr}" - ) + assert f"After filtering existing reviewers, adding: {expected_reviewers}" in proc.stdout + +@parameterize_named( # Missing expected tvm-ci/branch test - run( + missing_tvm_ci_branch=dict( statuses=[ { "context": "test", @@ -383,10 +301,9 @@ def run(statuses, expected_rc, expected_output): ], expected_rc=1, expected_output="No good commits found in the last 1 commits", - ) - + ), # Only has the right passing test - run( + has_expected_test=dict( statuses=[ { "context": "tvm-ci/branch", @@ -395,10 +312,9 @@ def run(statuses, expected_rc, expected_output): ], expected_rc=0, expected_output="Found last good commit: 123: hello", - ) - + ), # Check with many statuses - run( + many_statuses=dict( statuses=[ { "context": "tvm-ci/branch", @@ -415,8 +331,8 @@ def run(statuses, expected_rc, expected_output): ], expected_rc=1, expected_output="No good commits found in the last 1 commits", - ) - run( + ), + many_success_statuses=dict( statuses=[ { "context": "tvm-ci/branch", @@ -433,17 +349,50 @@ def run(statuses, expected_rc, expected_output): ], expected_rc=0, expected_output="Found last good commit: 123: hello", + ), +) +def test_update_branch(tmpdir_factory, statuses, expected_rc, expected_output): + """ + Test that the last-successful branch script updates successfully + """ + update_script = REPO_ROOT / "ci" / "scripts" / "update_branch.py" + + git = TempGit(tmpdir_factory.mktemp("tmp_git_dir")) + commit = { + "statusCheckRollup": {"contexts": {"nodes": statuses}}, + "oid": "123", + "messageHeadline": "hello", + } + data = { + "data": { + "repository": { + "defaultBranchRef": {"target": {"history": {"edges": [], "nodes": [commit]}}} + } + } + } + proc = run_script( + [update_script, "--dry-run", "--testonly-json", json.dumps(data)], + cwd=git.cwd, + check=False, ) + if proc.returncode != expected_rc: + raise RuntimeError(f"Wrong return code:\nstdout:\n{proc.stdout}\n\nstderr:\n{proc.stderr}") + + if expected_output not in proc.stdout: + raise RuntimeError( + f"Missing {expected_output}:\nstdout:\n{proc.stdout}\n\nstderr:\n{proc.stderr}" + ) + @parameterize_named( - dict( + dont_skip_main=dict( commands=[], should_skip=False, pr_title="[skip ci] test", why="ci should not be skipped on main", ), - dict( + dont_skip_main_with_commit=dict( commands=[ ["commit", "--allow-empty", "--message", "[skip ci] commit 1"], ], @@ -451,7 +400,7 @@ def run(statuses, expected_rc, expected_output): pr_title="[skip ci] test", why="ci should not be skipped on main", ), - dict( + skip_on_new_branch=dict( commands=[ ["checkout", "-b", "some_new_branch"], ["commit", "--allow-empty", "--message", "[skip ci] commit 1"], @@ -460,7 +409,7 @@ def run(statuses, expected_rc, expected_output): pr_title="[skip ci] test", why="ci should be skipped on a branch with [skip ci] in the last commit", ), - dict( + no_skip_in_pr_title=dict( commands=[ ["checkout", "-b", "some_new_branch"], ["commit", "--allow-empty", "--message", "[skip ci] commit 1"], @@ -470,7 +419,7 @@ def run(statuses, expected_rc, expected_output): why="ci should not be skipped on a branch with " "[skip ci] in the last commit but not the PR title", ), - dict( + skip_in_pr_title=dict( commands=[ ["checkout", "-b", "some_new_branch"], ["commit", "--allow-empty", "--message", "[skip ci] commit 1"], @@ -480,17 +429,7 @@ def run(statuses, expected_rc, expected_output): pr_title="[skip ci] test", why="ci should be skipped with [skip ci] in the PR title", ), - dict( - commands=[ - ["checkout", "-b", "some_new_branch"], - ["commit", "--allow-empty", "--message", "[skip ci] commit 1"], - ["commit", "--allow-empty", "--message", "commit 2"], - ], - should_skip=True, - pr_title="[skip ci] test", - why="ci should be skipped with [skip ci] in the PR title", - ), - dict( + skip_in_pr_title_many_commits=dict( commands=[ ["checkout", "-b", "some_new_branch"], ["commit", "--allow-empty", "--message", "commit 1"], @@ -502,7 +441,7 @@ def run(statuses, expected_rc, expected_output): pr_title="[skip ci] test", why="ci should be skipped with [skip ci] in the PR title", ), - dict( + skip_anywhere_in_title=dict( commands=[ ["checkout", "-b", "some_new_branch"], ], @@ -518,22 +457,16 @@ def test_skip_ci(tmpdir_factory, commands, should_skip, pr_title, why): skip_ci_script = REPO_ROOT / "ci" / "scripts" / "git_skip_ci.py" git = TempGit(tmpdir_factory.mktemp("tmp_git_dir")) - # Jenkins git is too old and doesn't have 'git init --initial-branch' - git.run("init") - git.run("checkout", "-b", "main") - git.run("remote", "add", "origin", "https://github.com/apache/tvm.git") + git.run("config", "user.name", "ci") git.run("config", "user.email", "email@example.com") git.run("commit", "--allow-empty", "--message", "base commit") for command in commands: git.run(*command) pr_number = "1234" - proc = subprocess.run( - [str(skip_ci_script), "--pr", pr_number, "--pr-title", pr_title], + proc = run_script( + [skip_ci_script, "--pr", pr_number, "--pr-title", pr_title], cwd=git.cwd, - stderr=subprocess.STDOUT, - stdout=subprocess.PIPE, - encoding="utf-8", check=False, ) expected = 0 if should_skip else 1 @@ -544,120 +477,66 @@ def test_skip_ci(tmpdir_factory, commands, should_skip, pr_title, why): ) -def test_skip_globs(tmpdir_factory): +@parameterize_named( + no_file=dict(files=[], should_skip=True), + readme=dict(files=["README.md"], should_skip=True), + c_file=dict(files=["test.c"], should_skip=False), + c_and_readme=dict(files=["test.c", "README.md"], should_skip=False), + src_file_and_readme=dict( + files=["src/autotvm/feature_visitor.cc", "README.md"], should_skip=False + ), + yaml_and_readme=dict(files=[".asf.yaml", "docs/README.md"], should_skip=True), +) +def test_skip_globs(tmpdir_factory, files, should_skip): """ Test that CI is skipped if only certain files are edited """ script = REPO_ROOT / "ci" / "scripts" / "git_skip_ci_globs.py" - def run(files, should_skip): - git = TempGit(tmpdir_factory.mktemp("tmp_git_dir")) - # Jenkins git is too old and doesn't have 'git init --initial-branch' - git.run("init") - git.run("checkout", "-b", "main") - git.run("remote", "add", "origin", "https://github.com/apache/tvm.git") - - proc = subprocess.run( - [ - str(script), - "--files", - ",".join(files), - ], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - encoding="utf-8", - cwd=git.cwd, - check=False, - ) - - if should_skip: - assert proc.returncode == 0 - else: - assert proc.returncode == 1 - - run([], should_skip=True) - run(["README.md"], should_skip=True) - run(["test.c"], should_skip=False) - run(["test.c", "README.md"], should_skip=False) - run(["src/autotvm/feature_visitor.cc", "README.md"], should_skip=False) - run([".asf.yaml", "docs/README.md"], should_skip=True) + git = TempGit(tmpdir_factory.mktemp("tmp_git_dir")) + proc = run_script( + [ + script, + "--files", + ",".join(files), + ], + check=False, + cwd=git.cwd, + ) -def test_ping_reviewers(tmpdir_factory): - """ - Test that reviewers are messaged after a time period of inactivity - """ - reviewers_script = REPO_ROOT / "ci" / "scripts" / "ping_reviewers.py" + if should_skip: + assert proc.returncode == 0 + else: + assert proc.returncode == 1 - def run(pull_request, check): - git = TempGit(tmpdir_factory.mktemp("tmp_git_dir")) - # Jenkins git is too old and doesn't have 'git init --initial-branch' - git.run("init") - git.run("checkout", "-b", "main") - git.run("remote", "add", "origin", "https://github.com/apache/tvm.git") - - data = { - "data": { - "repository": { - "pullRequests": { - "nodes": [pull_request], - "edges": [], - } - } - } - } - proc = subprocess.run( - [ - str(reviewers_script), - "--dry-run", - "--wait-time-minutes", - "1", - "--cutoff-pr-number", - "5", - "--allowlist", - "user", - "--pr-json", - json.dumps(data), - "--now", - "2022-01-26T17:54:19Z", - ], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - encoding="utf-8", - cwd=git.cwd, - check=False, - ) - if proc.returncode != 0: - raise RuntimeError(f"Process failed:\nstdout:\n{proc.stdout}\n\nstderr:\n{proc.stderr}") - assert check in proc.stdout +def all_time_keys(time): + return { + "updatedAt": time, + "lastEditedAt": time, + "createdAt": time, + "publishedAt": time, + } - def all_time_keys(time): - return { - "updatedAt": time, - "lastEditedAt": time, - "createdAt": time, - "publishedAt": time, - } - run( - { +@parameterize_named( + draft=dict( + pull_request={ "isDraft": True, "number": 2, }, - "Checking 0 of 1 fetched", - ) - - run( - { + check="Checking 0 of 1 fetched", + ), + not_draft=dict( + pull_request={ "isDraft": False, "number": 2, }, - "Checking 0 of 1 fetched", - ) - - run( - { + check="Checking 0 of 1 fetched", + ), + week_old=dict( + pull_request={ "number": 123, "url": "https://github.com/apache/tvm/pull/123", "body": "cc @someone", @@ -667,12 +546,11 @@ def all_time_keys(time): **all_time_keys("2022-01-18T17:54:19Z"), "comments": {"nodes": []}, }, - "Pinging reviewers ['someone'] on https://github.com/apache/tvm/pull/123", - ) - + check="Pinging reviewers ['someone'] on https://github.com/apache/tvm/pull/123", + ), # Check allowlist functionality - run( - { + allowlist=dict( + pull_request={ "number": 123, "url": "https://github.com/apache/tvm/pull/123", "body": "cc @someone", @@ -686,12 +564,11 @@ def all_time_keys(time): ] }, }, - "Checking 0 of 1 fetched", - ) - + check="Checking 0 of 1 fetched", + ), # Old comment, ping - run( - { + old_comment=dict( + pull_request={ "number": 123, "url": "https://github.com/apache/tvm/pull/123", "body": "cc @someone", @@ -708,12 +585,11 @@ def all_time_keys(time): ] }, }, - "Pinging reviewers ['someone'] on https://github.com/apache/tvm/pull/123", - ) - + check="Pinging reviewers ['someone'] on https://github.com/apache/tvm/pull/123", + ), # New comment, don't ping - run( - { + new_comment=dict( + pull_request={ "number": 123, "url": "https://github.com/apache/tvm/pull/123", "body": "cc @someone", @@ -727,8 +603,45 @@ def all_time_keys(time): ] }, }, - "Not pinging PR 123", + check="Not pinging PR 123", + ), +) +def test_ping_reviewers(tmpdir_factory, pull_request, check): + """ + Test that reviewers are messaged after a time period of inactivity + """ + reviewers_script = REPO_ROOT / "ci" / "scripts" / "ping_reviewers.py" + + git = TempGit(tmpdir_factory.mktemp("tmp_git_dir")) + + data = { + "data": { + "repository": { + "pullRequests": { + "nodes": [pull_request], + "edges": [], + } + } + } + } + proc = run_script( + [ + reviewers_script, + "--dry-run", + "--wait-time-minutes", + "1", + "--cutoff-pr-number", + "5", + "--allowlist", + "user", + "--pr-json", + json.dumps(data), + "--now", + "2022-01-26T17:54:19Z", + ], + cwd=git.cwd, ) + assert_in(check, proc.stdout) def assert_in(needle: str, haystack: str): @@ -740,69 +653,8 @@ def assert_in(needle: str, haystack: str): @tvm.testing.skip_if_wheel_test -def test_github_tag_teams(tmpdir_factory): - """ - Check that individuals are tagged from team headers - """ - tag_script = REPO_ROOT / "ci" / "scripts" / "github_tag_teams.py" - - def run(source_type, data, check): - git = TempGit(tmpdir_factory.mktemp("tmp_git_dir")) - git.run("init") - git.run("checkout", "-b", "main") - git.run("remote", "add", "origin", "https://github.com/apache/tvm.git") - - issue_body = """ - some text - [temporary] opt-in: @person5 - - - something: @person1 @person2 - - something3: @person1 @person2 @SOME1-ONE- - - something else @person1 @person2 - - something else2: @person1 @person2 - - something-else @person1 @person2 - """ - comment1 = """ - another thing: @person3 - another-thing @person3 - """ - comment2 = """ - something @person4 - @person5 - """ - teams = { - "data": { - "repository": { - "issue": { - "body": issue_body, - "comments": {"nodes": [{"body": comment1}, {"body": comment2}]}, - } - } - } - } - env = { - source_type: json.dumps(data), - } - proc = subprocess.run( - [ - str(tag_script), - "--dry-run", - "--team-issue-json", - json.dumps(teams), - ], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - encoding="utf-8", - cwd=git.cwd, - env=env, - check=False, - ) - if proc.returncode != 0: - raise RuntimeError(f"Process failed:\nstdout:\n{proc.stdout}\n\nstderr:\n{proc.stderr}") - - assert_in(check, proc.stdout) - - run( +@parameterize_named( + no_cc=dict( source_type="ISSUE", data={ "title": "A title", @@ -818,9 +670,8 @@ def run(source_type, data, check): ), }, check="No one to cc, exiting", - ) - - run( + ), + no_additional_cc=dict( source_type="ISSUE", data={ "title": "A title", @@ -838,9 +689,8 @@ def run(source_type, data, check): ), }, check="No one to cc, exiting", - ) - - run( + ), + cc_update=dict( source_type="ISSUE", data={ "title": "A title", @@ -858,9 +708,8 @@ def run(source_type, data, check): }, check="would have updated issues/1234 with {'body': " "'\\nhello\\n\\nsomething\\n\\ncc @person1 @person2 @person4'}", - ) - - run( + ), + already_cced=dict( source_type="ISSUE", data={ "title": "A title", @@ -877,9 +726,8 @@ def run(source_type, data, check): ), }, check="No one to cc, exiting", - ) - - run( + ), + not_already_cced=dict( source_type="ISSUE", data={ "title": "[something] A title", @@ -897,9 +745,8 @@ def run(source_type, data, check): }, check="would have updated issues/1234 with {'body': " "'\\nhello\\n\\nsomething\\n\\ncc @person1 @person2 @person4'}", - ) - - run( + ), + no_new_ccs=dict( source_type="ISSUE", data={ "title": "[something] A title", @@ -916,9 +763,8 @@ def run(source_type, data, check): ), }, check="No one to cc, exiting", - ) - - run( + ), + mismatching_tags=dict( source_type="PR", data={ "title": "[something] A title", @@ -936,9 +782,8 @@ def run(source_type, data, check): ), }, check="No one to cc, exiting", - ) - - run( + ), + draft_pr=dict( source_type="PR", data={ "title": "[something] A title", @@ -956,9 +801,8 @@ def run(source_type, data, check): ), }, check="Terminating since 1234 is a draft", - ) - - run( + ), + edit_inplace=dict( source_type="ISSUE", data={ "title": "[something] A title", @@ -974,9 +818,8 @@ def run(source_type, data, check): check="would have updated issues/1234 with {'body': '`mold` and `lld` can be a much" " faster alternative to `ld` from gcc. We should modify our CMakeLists.txt to " "detect and use these when possible. cc @person1\\n\\ncc @person2 @person4'}", - ) - - run( + ), + edit_out_of_place=dict( source_type="ISSUE", data={ "title": "[something3] A title", @@ -989,9 +832,8 @@ def run(source_type, data, check): }, check="Dry run, would have updated issues/1234 with" " {'body': '@person2 @SOME1-ONE-\\n\\ncc @person1'}", - ) - - run( + ), + atted_but_not_cced=dict( source_type="ISSUE", data={ "title": "[] A title", @@ -1003,12 +845,64 @@ def run(source_type, data, check): "body": "@person2 @SOME1-ONE-", }, check="No one to cc, exiting", + ), +) +def test_github_tag_teams(tmpdir_factory, source_type, data, check): + """ + Check that individuals are tagged from team headers + """ + tag_script = REPO_ROOT / "ci" / "scripts" / "github_tag_teams.py" + + git = TempGit(tmpdir_factory.mktemp("tmp_git_dir")) + + issue_body = """ + some text + [temporary] opt-in: @person5 + + - something: @person1 @person2 + - something3: @person1 @person2 @SOME1-ONE- + - something else @person1 @person2 + - something else2: @person1 @person2 + - something-else @person1 @person2 + """ + comment1 = """ + another thing: @person3 + another-thing @person3 + """ + comment2 = """ + something @person4 + @person5 + """ + teams = { + "data": { + "repository": { + "issue": { + "body": issue_body, + "comments": {"nodes": [{"body": comment1}, {"body": comment2}]}, + } + } + } + } + env = { + source_type: json.dumps(data), + } + proc = run_script( + [ + tag_script, + "--dry-run", + "--team-issue-json", + json.dumps(teams), + ], + cwd=git.cwd, + env=env, ) + assert_in(check, proc.stdout) + @tvm.testing.skip_if_wheel_test @parameterize_named( - dict( + same_tags=dict( tlcpackstaging_body={ "results": [ { @@ -1028,7 +922,7 @@ def run(source_type, data, check): expected="Tag names were the same, no update needed", expected_images=[], ), - dict( + staging_update=dict( tlcpackstaging_body={ "results": [ { @@ -1054,7 +948,7 @@ def run(source_type, data, check): "ci_arm = 'tlcpack/ci-arm:456-456-abc'", ], ), - dict( + tlcpack_update=dict( tlcpackstaging_body={ "results": [ { @@ -1084,22 +978,19 @@ def test_open_docker_update_pr( tag_script = REPO_ROOT / "ci" / "scripts" / "open_docker_update_pr.py" git = TempGit(tmpdir_factory.mktemp("tmp_git_dir")) - git.run("init") git.run("config", "user.name", "ci") git.run("config", "user.email", "email@example.com") - git.run("checkout", "-b", "main") - git.run("remote", "add", "origin", "https://github.com/apache/tvm.git") images = [ - "ci_lint", - "ci_gpu", - "ci_cpu", - "ci_minimal", - "ci_wasm", - "ci_i386", - "ci_cortexm", "ci_arm", + "ci_cortexm", + "ci_cpu", + "ci_gpu", "ci_hexagon", + "ci_i386", + "ci_lint", + "ci_minimal", "ci_riscv", + "ci_wasm", ] docker_data = {} @@ -1107,52 +998,43 @@ def test_open_docker_update_pr( docker_data[f"repositories/tlcpackstaging/{image}/tags"] = tlcpackstaging_body docker_data[f"repositories/tlcpack/{image.replace('_', '-')}/tags"] = tlcpack_body - proc = subprocess.run( + proc = run_script( [ - str(tag_script), + tag_script, "--dry-run", "--testing-docker-data", json.dumps(docker_data), ], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - encoding="utf-8", cwd=git.cwd, env={"GITHUB_TOKEN": "1234"}, - check=False, + stderr=subprocess.STDOUT, ) for line in expected_images: if line not in proc.stdout: raise RuntimeError(f"Missing line {line} in output:\n{proc.stdout}") - if proc.returncode != 0: - raise RuntimeError(f"Process failed:\nstdout:\n{proc.stdout}\n\nstderr:\n{proc.stderr}") - assert_in(expected, proc.stdout) -@pytest.mark.parametrize( - "images,expected", - [ - ( - ["ci_arm=tlcpack/ci-arm:abc-abc-123", "ci_lint=tlcpack/ci-lint:abc-abc-234"], - { - "ci_arm": "tlcpack/ci-arm:abc-abc-123", - "ci_lint": "tlcpack/ci-lint:abc-abc-234", - }, - ), - ( - ["ci_arm2=tlcpack/ci-arm2:abc-abc-123"], - { - "ci_arm2": "tlcpackstaging/ci_arm2:abc-abc-123", - }, - ), - ], +@parameterize_named( + use_tlcpack=dict( + images=["ci_arm=tlcpack/ci-arm:abc-abc-123", "ci_lint=tlcpack/ci-lint:abc-abc-234"], + expected={ + "ci_arm": "tlcpack/ci-arm:abc-abc-123", + "ci_lint": "tlcpack/ci-lint:abc-abc-234", + }, + ), + use_staging=dict( + images=["ci_arm2=tlcpack/ci-arm2:abc-abc-123"], + expected={ + "ci_arm2": "tlcpackstaging/ci_arm2:abc-abc-123", + }, + ), ) def test_determine_docker_images(tmpdir_factory, images, expected): """Test script to decide whether to use tlcpack or tlcpackstaging for images""" - tag_script = REPO_ROOT / "ci" / "scripts" / "determine_docker_images.py" + script = REPO_ROOT / "ci" / "scripts" / "determine_docker_images.py" git_dir = tmpdir_factory.mktemp("tmp_git_dir") @@ -1161,23 +1043,17 @@ def test_determine_docker_images(tmpdir_factory, images, expected): "repositories/tlcpack/ci-lint/tags/abc-abc-234": {}, } - proc = subprocess.run( + run_script( [ - str(tag_script), + script, "--testing-docker-data", json.dumps(docker_data), "--base-dir", git_dir, ] + images, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - encoding="utf-8", cwd=git_dir, - check=False, ) - if proc.returncode != 0: - raise RuntimeError(f"Failed to run script:\n{proc.stdout}") for expected_filename, expected_image in expected.items(): with open(Path(git_dir) / expected_filename) as f: @@ -1186,34 +1062,28 @@ def test_determine_docker_images(tmpdir_factory, images, expected): assert actual_image == expected_image -@pytest.mark.parametrize( - "changed_files,name,check,expected_code", - [ - d.values() - for d in [ - dict( - changed_files=[], - name="abc", - check="Image abc is not using new naming scheme", - expected_code=1, - ), - dict( - changed_files=[], name="123-123-abc", check="No extant hash found", expected_code=1 - ), - dict( - changed_files=[["test.txt"]], - name=None, - check="Did not find changes, no rebuild necessary", - expected_code=0, - ), - dict( - changed_files=[["test.txt"], ["docker/test.txt"]], - name=None, - check="Found docker changes", - expected_code=2, - ), - ] - ], +@parameterize_named( + invalid_name=dict( + changed_files=[], + name="abc", + check="Image abc is not using new naming scheme", + expected_code=1, + ), + no_hash=dict( + changed_files=[], name="123-123-abc", check="No extant hash found", expected_code=1 + ), + no_changes=dict( + changed_files=[["test.txt"]], + name=None, + check="Did not find changes, no rebuild necessary", + expected_code=0, + ), + docker_changes=dict( + changed_files=[["test.txt"], ["docker/test.txt"]], + name=None, + check="Found docker changes", + expected_code=2, + ), ) def test_should_rebuild_docker(tmpdir_factory, changed_files, name, check, expected_code): """ @@ -1222,11 +1092,8 @@ def test_should_rebuild_docker(tmpdir_factory, changed_files, name, check, expec tag_script = REPO_ROOT / "ci" / "scripts" / "should_rebuild_docker.py" git = TempGit(tmpdir_factory.mktemp("tmp_git_dir")) - git.run("init") git.run("config", "user.name", "ci") git.run("config", "user.email", "email@example.com") - git.run("checkout", "-b", "main") - git.run("remote", "add", "origin", "https://github.com/apache/tvm.git") git_path = Path(git.cwd) for i, commits in enumerate(changed_files): @@ -1262,15 +1129,13 @@ def test_should_rebuild_docker(tmpdir_factory, changed_files, name, check, expec }, } - proc = subprocess.run( + proc = run_script( [ - str(tag_script), + tag_script, "--testing-docker-data", json.dumps(docker_data), ], - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - encoding="utf-8", cwd=git.cwd, check=False, ) diff --git a/tests/python/ci/test_tvmbot.py b/tests/python/ci/test_tvmbot.py index 2c7a0eaec0d4..ceabd46a9b03 100644 --- a/tests/python/ci/test_tvmbot.py +++ b/tests/python/ci/test_tvmbot.py @@ -18,13 +18,12 @@ Test the @tvm-bot merge code """ -import subprocess import json from pathlib import Path +from typing import Dict, Any -import pytest import tvm -from .test_utils import REPO_ROOT, TempGit +from .test_utils import REPO_ROOT, TempGit, run_script SUCCESS_EXPECTED_OUTPUT = """ @@ -37,167 +36,244 @@ """.strip() -TEST_DATA = { - "successful-merge": { - "number": 10786, - "filename": "pr10786-merges.json", - "expected": SUCCESS_EXPECTED_OUTPUT, - "comment": "@tvm-bot merge", - "user": "abc", - "detail": "Everything is fine so this PR will merge", - }, - "no-request": { - "number": 10786, - "filename": "pr10786-nottriggered.json", - "expected": "Command 'do something else' did not match anything", - "comment": "@tvm-bot do something else", - "user": "abc", - "detail": "A PR for which the mergebot runs but no merge is requested", - }, - "bad-ci": { - "number": 10786, - "filename": "pr10786-badci.json", - "expected": "Cannot merge, these CI jobs are not successful on", - "comment": "@tvm-bot merge", - "user": "abc", - "detail": "A PR which failed CI and cannot merge", - }, - "old-review": { - "number": 10786, - "filename": "pr10786-oldreview.json", - "expected": "Cannot merge, did not find any approving reviews", - "comment": "@tvm-bot merge", - "user": "abc", - "detail": "A PR with passing CI and approving reviews on an old commit so it cannot merge", - }, - "missing-job": { - "number": 10786, - "filename": "pr10786-missing-job.json", - "expected": "Cannot merge, missing expected jobs", - "comment": "@tvm-bot merge", - "user": "abc", - "detail": "PR missing an expected CI job and cannot merge", - }, - "invalid-author": { - "number": 10786, - "filename": "pr10786-invalid-author.json", - "expected": "Failed auth check 'collaborators', quitting", - "comment": "@tvm-bot merge", - "user": "not-abc", - "detail": "Merge requester is not a committer and cannot merge", - }, - "unauthorized-comment": { - "number": 11244, - "filename": "pr11244-unauthorized-comment.json", - "expected": "Failed auth check 'collaborators'", - "comment": "@tvm-bot merge", - "user": "not-abc2", - "detail": "Check that a merge comment not from a CONTRIBUTOR is rejected", - }, - "no-review": { - "number": 11267, - "filename": "pr11267-no-review.json", - "expected": "Cannot merge, did not find any approving reviews from users with write access", - "comment": "@tvm-bot merge", - "user": "abc", - "detail": "Check that a merge request without any reviews is rejected", - }, - "changes-requested": { - "number": 10786, - "filename": "pr10786-changes-requested.json", - "expected": "Cannot merge, found [this review]", - "comment": "@tvm-bot merge", - "user": "abc", - "detail": "Check that a merge request with a 'Changes Requested' review is rejected", - }, - "co-authors": { - "number": 10786, - "filename": "pr10786-co-authors.json", - "expected": "Co-authored-by: Some One ", - "comment": "@tvm-bot merge", - "user": "abc", - "detail": "Check that a merge request with co-authors generates the correct commit message", - }, - "rerun-ci": { - "number": 11442, - "filename": "pr11442-rerun-ci.json", - "expected": "Rerunning ci with", - "comment": "@tvm-bot rerun", - "user": "abc", - "detail": "Start a new CI job", - }, - "ignore-jobs": { - "number": 10786, - "filename": "pr10786-ignore-jobs.json", - "expected": "Dry run, would have merged", - "comment": "@tvm-bot merge", - "user": "abc", - "detail": "Ignore GitHub Actions jobs that don't start with CI / ", - }, -} +class _TvmBotTest: + NUMBER = 10786 + + def preprocess_data(self, data: Dict[str, Any]): + """ + Used to pre-process PR data before running the test. Override as + necessary to edit data for specific test cases. + """ + return data + + @tvm.testing.skip_if_wheel_test + def test(self, tmpdir_factory): + """ + Run the tvm-bot script using the data from preprocess_data + """ + mergebot_script = REPO_ROOT / "ci" / "scripts" / "github_tvmbot.py" + test_json_dir = Path(__file__).resolve().parent / "sample_prs" + with open(test_json_dir / f"pr{self.NUMBER}.json") as f: + test_data = json.load(f) + + # Update testing data with replacements / additions + test_data = self.preprocess_data(test_data) + + git = TempGit(tmpdir_factory.mktemp("tmp_git_dir")) + + comment = { + "body": self.COMMENT, + "id": 123, + "user": { + "login": self.USER, + }, + } + allowed_users = [{"login": "abc"}, {"login": "other-abc"}] + + proc = run_script( + [ + mergebot_script, + "--pr", + self.NUMBER, + "--dry-run", + "--run-url", + "https://example.com", + "--testing-pr-json", + json.dumps(test_data), + "--testing-collaborators-json", + json.dumps(allowed_users), + "--testing-mentionable-users-json", + json.dumps(allowed_users), + "--trigger-comment-json", + json.dumps(comment), + ], + env={ + "TVM_BOT_JENKINS_TOKEN": "123", + "GH_ACTIONS_TOKEN": "123", + }, + cwd=git.cwd, + ) + + if self.EXPECTED not in proc.stderr: + raise RuntimeError(f"{proc.stderr}\ndid not contain\n{self.EXPECTED}") + + +class TestNoRequest(_TvmBotTest): + """ + A PR for which the mergebot runs but no merge is requested + """ + + COMMENT = "@tvm-bot do something else" + USER = "abc" + EXPECTED = "Command 'do something else' did not match anything" + + def preprocess_data(self, data: Dict[str, Any]): + data["reviews"]["nodes"][0]["body"] = "nothing" + return data + + +class TestSuccessfulMerge(_TvmBotTest): + """ + Everything is fine so this PR will merge + """ + + COMMENT = "@tvm-bot merge" + USER = "abc" + EXPECTED = SUCCESS_EXPECTED_OUTPUT + + +class TestBadCI(_TvmBotTest): + """ + A PR which failed CI and cannot merge + """ + + COMMENT = "@tvm-bot merge" + USER = "abc" + EXPECTED = "Cannot merge, these CI jobs are not successful on" + + def preprocess_data(self, data: Dict[str, Any]): + # Mark the Jenkins build as failed + contexts = data["commits"]["nodes"][0]["commit"]["statusCheckRollup"]["contexts"]["nodes"] + for context in contexts: + if "context" in context and context["context"] == "tvm-ci/pr-head": + context["state"] = "FAILED" + return data + + +class TestOldReview(_TvmBotTest): + """ + A PR with passing CI and approving reviews on an old commit so it cannot merge + """ + + COMMENT = "@tvm-bot merge" + USER = "abc" + EXPECTED = "Cannot merge, did not find any approving reviews" + + def preprocess_data(self, data: Dict[str, Any]) -> Dict[str, Any]: + data["reviews"]["nodes"][0]["commit"]["oid"] = "abc12345" + return data + + +class TestMissingJob(_TvmBotTest): + """ + PR missing an expected CI job and cannot merge + """ + + COMMENT = "@tvm-bot merge" + USER = "abc" + EXPECTED = "Cannot merge, missing expected jobs" + + def preprocess_data(self, data: Dict[str, Any]) -> Dict[str, Any]: + contexts = data["commits"]["nodes"][0]["commit"]["statusCheckRollup"]["contexts"]["nodes"] + for context in contexts: + if "context" in context and context["context"] == "tvm-ci/pr-head": + context["context"] = "something" + return data + + +class TestInvalidAuthor(_TvmBotTest): + """ + Merge requester is not a committer and cannot merge + """ + + COMMENT = "@tvm-bot merge" + USER = "not-abc" + EXPECTED = "Failed auth check 'collaborators', quitting" -@tvm.testing.skip_if_wheel_test -@pytest.mark.parametrize( - ["number", "filename", "expected", "comment", "user", "detail"], - [tuple(d.values()) for d in TEST_DATA.values()], - ids=TEST_DATA.keys(), -) -def test_tvmbot(tmpdir_factory, number, filename, expected, comment, user, detail): - """ - Test the mergebot test cases - """ - mergebot_script = REPO_ROOT / "ci" / "scripts" / "github_tvmbot.py" - test_json_dir = Path(__file__).resolve().parent / "sample_prs" - - git = TempGit(tmpdir_factory.mktemp("tmp_git_dir")) - git.run("init", stderr=subprocess.PIPE, stdout=subprocess.PIPE) - git.run("checkout", "-b", "main", stderr=subprocess.PIPE, stdout=subprocess.PIPE) - git.run("remote", "add", "origin", "https://github.com/apache/tvm.git") - with open(test_json_dir / filename) as f: - test_data = json.load(f) - - comment = { - "body": comment, - "id": 123, - "user": { - "login": user, - }, - } - allowed_users = [{"login": "abc"}] - - proc = subprocess.run( - [ - str(mergebot_script), - "--pr", - str(number), - "--dry-run", - "--run-url", - "https://example.com", - "--testing-pr-json", - json.dumps(test_data), - "--testing-collaborators-json", - json.dumps(allowed_users), - "--testing-mentionable-users-json", - json.dumps(allowed_users), - "--trigger-comment-json", - json.dumps(comment), - ], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - encoding="utf-8", - env={ - "TVM_BOT_JENKINS_TOKEN": "123", - "GH_ACTIONS_TOKEN": "123", - }, - cwd=git.cwd, - check=False, - ) - if proc.returncode != 0: - raise RuntimeError(f"Process failed:\nstdout:\n{proc.stdout}\n\nstderr:\n{proc.stderr}") - - if expected not in proc.stderr: - raise RuntimeError(f"{proc.stderr}\ndid not contain\n{expected}") +class TestUnauthorizedComment(_TvmBotTest): + """ + Check that a merge comment not from a CONTRIBUTOR is rejected + """ + + COMMENT = "@tvm-bot merge" + USER = "not-abc2" + EXPECTED = "Failed auth check 'collaborators'" + + +class TestNoReview(_TvmBotTest): + """ + Check that a merge request without any reviews is rejected + """ + + COMMENT = "@tvm-bot merge" + USER = "abc" + EXPECTED = "Cannot merge, did not find any approving reviews from users with write access" + + def preprocess_data(self, data: Dict[str, Any]) -> Dict[str, Any]: + data["reviews"]["nodes"] = [] + return data + + +class TestChangesRequested(_TvmBotTest): + """ + Check that a merge request with a 'Changes Requested' review is rejected + """ + + COMMENT = "@tvm-bot merge" + USER = "abc" + EXPECTED = "Cannot merge, found [this review]" + + def preprocess_data(self, data: Dict[str, Any]) -> Dict[str, Any]: + data["reviews"]["nodes"][0]["state"] = "CHANGES_REQUESTED" + data["reviews"]["nodes"][0]["url"] = "http://example.com" + return data + + +class TestCoAuthors(_TvmBotTest): + """ + Check that a merge request with co-authors generates the correct commit message + """ + + COMMENT = "@tvm-bot merge" + USER = "abc" + EXPECTED = "Co-authored-by: Some One " + + def preprocess_data(self, data: Dict[str, Any]) -> Dict[str, Any]: + data["authorCommits"]["nodes"][0]["commit"]["authors"]["nodes"].append( + {"name": "Some One", "email": "someone@email.com"} + ) + return data + + +class TestRerunCI(_TvmBotTest): + """ + Start a new CI job + """ + + COMMENT = "@tvm-bot rerun" + USER = "abc" + EXPECTED = "Rerunning ci with" + + +class TestRerunPermissions(_TvmBotTest): + """ + Start a new CI job as an unauthorized user + """ + + COMMENT = "@tvm-bot rerun" + USER = "someone" + EXPECTED = "Failed auth check 'metionable_users', quitting" + + +class TestRerunNonAuthor(_TvmBotTest): + """ + Start a new CI job as a mentionable user + """ + + COMMENT = "@tvm-bot rerun" + USER = "other-abc" + EXPECTED = "Passed auth check 'metionable_users', continuing" + + +class TestIgnoreJobs(_TvmBotTest): + """ + Ignore GitHub Actions jobs that don't start with CI / + """ + + COMMENT = "@tvm-bot merge" + USER = "abc" + EXPECTED = "Dry run, would have merged" if __name__ == "__main__": diff --git a/tests/python/ci/test_utils.py b/tests/python/ci/test_utils.py index 513601aa1b46..4a0f2710e74a 100644 --- a/tests/python/ci/test_utils.py +++ b/tests/python/ci/test_utils.py @@ -19,19 +19,28 @@ """ import subprocess import pathlib +from typing import List, Any REPO_ROOT = pathlib.Path(__file__).resolve().parent.parent.parent.parent class TempGit: """ - A wrapper to run commands in a directory + A wrapper to run commands in a directory (specifically for use in CI tests) """ def __init__(self, cwd): self.cwd = cwd + # Jenkins git is too old and doesn't have 'git init --initial-branch', + # so init and checkout need to be separate steps + self.run("init", stderr=subprocess.PIPE, stdout=subprocess.PIPE) + self.run("checkout", "-b", "main", stderr=subprocess.PIPE) + self.run("remote", "add", "origin", "https://github.com/apache/tvm.git") def run(self, *args, **kwargs): + """ + Run a git command based on *args + """ proc = subprocess.run( ["git"] + list(args), encoding="utf-8", cwd=self.cwd, check=False, **kwargs ) @@ -39,3 +48,25 @@ def run(self, *args, **kwargs): raise RuntimeError(f"git command failed: '{args}'") return proc + + +def run_script(command: List[Any], check: bool = True, **kwargs): + """ + Wrapper to run a script and print its output if there was an error + """ + command = [str(c) for c in command] + kwargs_to_send = { + "stdout": subprocess.PIPE, + "stderr": subprocess.PIPE, + "encoding": "utf-8", + } + kwargs_to_send.update(kwargs) + proc = subprocess.run( + command, + check=False, + **kwargs_to_send, + ) + if check and proc.returncode != 0: + raise RuntimeError(f"Process failed:\nstdout:\n{proc.stdout}\n\nstderr:\n{proc.stderr}") + + return proc