Skip to content

Commit

Permalink
[misc] Check the previous Github workflow run status if CI is skipped (
Browse files Browse the repository at this point in the history
…taichi-dev#1837)

* [misc] Check the previous Github workflow run status if CI is skipped

* check prev run

* rm skip ci

* [skip ci] match workflow

* [skip ci] simplify workflow check

* [skip ci] needs format

* [skip ci] whatever

* [skip ci] intentionally break the build

* [skip ci] enforce code format

* [skip ci] revert broken

Co-authored-by: Taichi Gardener <[email protected]>
  • Loading branch information
k-ye and taichi-gardener authored Sep 6, 2020
1 parent b78489c commit 64a6122
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 0 deletions.
15 changes: 15 additions & 0 deletions .github/workflows/persubmit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,21 @@ jobs:
ti diagnose
ti test -vr2 -t2
check_previous_run:
name: Checks the Workflow Run of the Previous Commit
runs-on: ubuntu-latest
if: ${{ contains(github.event.pull_request.labels.*.name, 'skip ci') || github.event.sender.login == 'taichi-gardener' }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Check the previous run
run: |
PR=${{ github.event.pull_request.number }}
SHA=${{ github.event.pull_request.head.sha }}
python misc/ci_check_previous_run.py --pr ${PR} --sha ${SHA}
code_format:
name: Code Format
runs-on: ubuntu-latest
Expand Down
133 changes: 133 additions & 0 deletions misc/ci_check_previous_run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import argparse
import json
import logging
import time
import urllib.request as ur
import sys

API_PREFIX = 'https://api.github.com/repos/taichi-dev/taichi'
SHA = 'sha'


def make_api_url(p):
return f'{API_PREFIX}/{p}'


def send_request(url):
logging.debug(f'request={url}')
return ur.urlopen(url)


def get_commits(pr):
url = make_api_url(f'pulls/{pr}/commits')
f = send_request(url)
return json.loads(f.read())


def locate_previous_commit_sha(commits, head_sha):
"""
Returns the previous commit of |head_sha| in PR's |commits|.
"""
assert commits[-1][SHA] == head_sha
if len(commits) < 2:
return None
return commits[-2][SHA]


def get_workflow_runs(page_id):
# https://docs.github.com/en/rest/reference/actions#list-workflow-runs-for-a-repository
url = make_api_url(f'actions/runs?page={page_id}')
f = send_request(url)
return json.loads(f.read())


def is_desired_workflow(run_json):
"""
Checks if this run is for the "Presubmit Checks" workflow.
"""
# Each workflow has a fixed ID.
# For the "Persubmit Checks" workflow, it is:
# https://api.github.com/repos/taichi-dev/taichi/actions/workflows/1291024
DESIRED_ID = 1291024
return run_json['workflow_id'] == DESIRED_ID


def locate_workflow_run_id(sha):
done = False
page_id = 0
while not done:
# Note that the REST API to get runs paginates the results.
runs = get_workflow_runs(page_id)['workflow_runs']
for r in runs:
if r['head_sha'] == sha and is_desired_workflow(r):
return r['id']
page_id += 1
return ''


def get_status_of_run(run_id):
"""
Waits for run identified by |run_id| to complete and returns its status.
"""
url = make_api_url(f'actions/runs/{run_id}')
start = time.time()
retries = 0
MAX_TIMEOUT = 60 * 60 # 1 hour
while True:
f = send_request(url)
j = json.loads(f.read())
# https://developer.github.com/v3/checks/runs/#create-a-check-run
if j['status'] == 'completed':
c = j['conclusion']
logging.debug(f'run={run_id} conclusion={c}')
return c == 'success'

if time.time() - start > MAX_TIMEOUT:
return False
retries += 1
logging.info(
f'Waiting to get the status of run={run_id} (url={url}). retries={retries}'
)
time.sleep(15)
return False


def get_cmd_args():
parser = argparse.ArgumentParser()
parser.add_argument('--pr', help='PR number')
parser.add_argument('--sha', help='Head commit SHA in the PR')
return parser.parse_args()


def main():
logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s',
level=logging.DEBUG,
datefmt='%Y-%m-%d %H:%M:%S')
args = get_cmd_args()

pr = args.pr
commits = get_commits(pr)
num_commits = len(commits)
logging.debug(f'\nPR={pr} #commits={num_commits}')

head_sha = args.sha
prev_sha = locate_previous_commit_sha(commits, head_sha)
logging.debug(f'SHA: head={head_sha} prev={prev_sha}')
if prev_sha is None:
logging.info(f'First commit in PR={pr}, no previous run to check')
# First commit in the PR
return 0

run_id = locate_workflow_run_id(prev_sha)
if not run_id:
logging.warning(f'Could not find the workflow run for SHA={prev_sha}')
return 0

logging.info(f'Prev commit: SHA={prev_sha} workflow_run_id={run_id}')
run_ok = get_status_of_run(run_id)
logging.info(f'workflow_run_id={run_id} ok={run_ok}')
return 0 if run_ok else 1


if __name__ == '__main__':
sys.exit(main())

0 comments on commit 64a6122

Please sign in to comment.