-
Notifications
You must be signed in to change notification settings - Fork 3.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ci] improve experience with optional GitHub workflows #3740
Changes from all commits
04111bd
c8c3e47
371922c
49b1a87
581711c
1cb0606
d518ed0
84c28aa
57358c8
353d669
f633d7e
0f6225c
30c2f42
cccefa7
0592a12
7bae9fc
3203b95
67c57b6
84a6394
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
#!/bin/bash | ||
# | ||
# [description] | ||
# Update comment appending a given body to the specified original comment. | ||
# | ||
# [usage] | ||
# append_comment.sh <COMMENT_ID> <BODY> | ||
# | ||
# COMMENT_ID: ID of comment that should be modified. | ||
# | ||
# BODY: Text that will be appended to the original comment body. | ||
|
||
set -e | ||
|
||
if [ -z "$GITHUB_ACTIONS" ]; then | ||
echo "Must be run inside GitHub Actions CI" | ||
exit -1 | ||
fi | ||
|
||
if [ $# -ne 2 ]; then | ||
echo "Usage: $0 <COMMENT_ID> <BODY>" | ||
exit -1 | ||
fi | ||
|
||
comment_id=$1 | ||
body=$2 | ||
|
||
old_comment_body=$( | ||
curl -sL \ | ||
-H "Accept: application/vnd.github.v3+json" \ | ||
-H "Authorization: token $SECRETS_WORKFLOW" \ | ||
"${GITHUB_API_URL}/repos/microsoft/LightGBM/issues/comments/$comment_id" | \ | ||
jq '.body' | ||
) | ||
body=${body/failure/failure ❌} | ||
body=${body/error/failure ❌} | ||
body=${body/cancelled/failure ❌} | ||
body=${body/timed_out/failure ❌} | ||
body=${body/success/success ✔️} | ||
data=$( | ||
jq -n \ | ||
--argjson body "${old_comment_body%?}\r\n\r\n$body\"" \ | ||
'{"body":$body}' | ||
) | ||
curl -sL \ | ||
-X PATCH \ | ||
-H "Accept: application/vnd.github.v3+json" \ | ||
-H "Authorization: token $SECRETS_WORKFLOW" \ | ||
-d "$data" \ | ||
"${GITHUB_API_URL}/repos/microsoft/LightGBM/issues/comments/$comment_id" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
# coding: utf-8 | ||
"""Get the most recent status of workflow for the current PR.""" | ||
import json | ||
from os import environ | ||
from sys import argv, exit | ||
from time import sleep | ||
try: | ||
from urllib import request | ||
except ImportError: | ||
import urllib2 as request | ||
|
||
|
||
def get_runs(workflow_name): | ||
"""Get all triggering workflow comments in the current PR. | ||
|
||
Parameters | ||
---------- | ||
workflow_name : string | ||
Name of the workflow. | ||
|
||
Returns | ||
------- | ||
pr_runs : list | ||
List of comment objects sorted by the time of creation in decreasing order. | ||
""" | ||
pr_runs = [] | ||
if environ.get("GITHUB_EVENT_NAME", "") == "pull_request": | ||
pr_number = int(environ.get("GITHUB_REF").split('/')[-2]) | ||
req = request.Request(url="{}/repos/microsoft/LightGBM/issues/{}/comments".format(environ.get("GITHUB_API_URL"), | ||
pr_number), | ||
headers={"Accept": "application/vnd.github.v3+json"}) | ||
url = request.urlopen(req) | ||
data = json.loads(url.read().decode('utf-8')) | ||
url.close() | ||
pr_runs = [i for i in data | ||
if i['author_association'].lower() in {'owner', 'member', 'collaborator'} | ||
and i['body'].startswith('/gha run') | ||
and 'Workflow **{}** has been triggered!'.format(workflow_name) in i['body']] | ||
return pr_runs[::-1] | ||
|
||
|
||
def get_status(runs): | ||
"""Get the most recent status of workflow for the current PR. | ||
|
||
Parameters | ||
---------- | ||
runs : list | ||
List of comment objects sorted by the time of creation in decreasing order. | ||
|
||
Returns | ||
------- | ||
status : string | ||
The most recent status of workflow. | ||
Can be 'success', 'failure' or 'in-progress'. | ||
""" | ||
status = 'success' | ||
for run in runs: | ||
body = run['body'] | ||
if "Status: " in body: | ||
if "Status: skipped" in body: | ||
continue | ||
if "Status: failure" in body: | ||
status = 'failure' | ||
break | ||
if "Status: success" in body: | ||
status = 'success' | ||
break | ||
else: | ||
status = 'in-progress' | ||
break | ||
return status | ||
|
||
|
||
if __name__ == "__main__": | ||
workflow_name = argv[1] | ||
while True: | ||
status = get_status(get_runs(workflow_name)) | ||
if status != 'in-progress': | ||
break | ||
sleep(60) | ||
if status == 'failure': | ||
exit(1) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
#!/bin/bash | ||
# | ||
# [description] | ||
# Rerun specified workflow for given pull request. | ||
# | ||
# [usage] | ||
# rerun_workflow.sh <WORKFLOW_ID> <PR_NUMBER> <PR_BRANCH> | ||
# | ||
# WORKFLOW_ID: Identifier (config name of ID) of a workflow to be rerun. | ||
# | ||
# PR_NUMBER: Number of pull request for which workflow should be rerun. | ||
# | ||
# PR_BRANCH: Name of pull request's branch. | ||
|
||
set -e | ||
|
||
if [ -z "$GITHUB_ACTIONS" ]; then | ||
echo "Must be run inside GitHub Actions CI" | ||
exit -1 | ||
fi | ||
|
||
if [ $# -ne 3 ]; then | ||
echo "Usage: $0 <WORKFLOW_ID> <PR_NUMBER> <PR_BRANCH>" | ||
exit -1 | ||
fi | ||
|
||
workflow_id=$1 | ||
pr_number=$2 | ||
pr_branch=$3 | ||
|
||
runs=$( | ||
curl -sL \ | ||
-H "Accept: application/vnd.github.v3+json" \ | ||
-H "Authorization: token $SECRETS_WORKFLOW" \ | ||
"${GITHUB_API_URL}/repos/microsoft/LightGBM/actions/workflows/${workflow_id}/runs?event=pull_request&branch=${pr_branch}" | \ | ||
jq '.workflow_runs' | ||
) | ||
runs=$(echo $runs | jq --arg pr_number "$pr_number" --arg pr_branch "$pr_branch" 'map(select(.event == "pull_request" and ((.pull_requests | length) != 0 and (.pull_requests[0].number | tostring) == $pr_number or .head_branch == $pr_branch)))') | ||
runs=$(echo $runs | jq 'sort_by(.run_number) | reverse') | ||
|
||
if [[ $(echo $runs | jq 'length') -gt 0 ]]; then | ||
curl -sL \ | ||
-X POST \ | ||
-H "Accept: application/vnd.github.v3+json" \ | ||
-H "Authorization: token $SECRETS_WORKFLOW" \ | ||
"${GITHUB_API_URL}/repos/microsoft/LightGBM/actions/runs/$(echo $runs | jq '.[0].id')/rerun" | ||
fi |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
#!/bin/bash | ||
# | ||
# [description] | ||
# Set a status with a given name to the specified commit. | ||
# | ||
# [usage] | ||
# set_commit_status.sh <NAME> <STATUS> <SHA> | ||
# | ||
# NAME: Name of status. | ||
# Status with existing name overwrites a previous one. | ||
# | ||
# STATUS: Status to be set. | ||
# Can be "error", "failure", "pending" or "success". | ||
# | ||
# SHA: SHA of a commit to set a status on. | ||
|
||
set -e | ||
|
||
if [ -z "$GITHUB_ACTIONS" ]; then | ||
echo "Must be run inside GitHub Actions CI" | ||
exit -1 | ||
fi | ||
|
||
if [ $# -ne 3 ]; then | ||
echo "Usage: $0 <NAME> <STATUS> <SHA>" | ||
exit -1 | ||
fi | ||
|
||
name=$1 | ||
|
||
status=$2 | ||
status=${status/error/failure} | ||
status=${status/cancelled/failure} | ||
status=${status/timed_out/failure} | ||
status=${status/in_progress/pending} | ||
status=${status/queued/pending} | ||
|
||
sha=$3 | ||
|
||
data=$( | ||
jq -n \ | ||
--arg state $status \ | ||
--arg url "${GITHUB_SERVER_URL}/microsoft/LightGBM/actions/runs/${GITHUB_RUN_ID}" \ | ||
--arg name "$name" \ | ||
'{"state":$state,"target_url":$url,"context":$name}' | ||
) | ||
|
||
curl -sL \ | ||
-X POST \ | ||
-H "Accept: application/vnd.github.v3+json" \ | ||
-H "Authorization: token $SECRETS_WORKFLOW" \ | ||
-d "$data" \ | ||
"${GITHUB_API_URL}/repos/microsoft/LightGBM/statuses/$sha" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
#!/bin/bash | ||
# | ||
# [description] | ||
# Trigger manual workflow run by a dispatch event. | ||
# | ||
# [usage] | ||
# trigger_dispatch_run.sh <PR_URL> <COMMENT_ID> <DISPATCH_NAME> | ||
# | ||
# PR_URL: URL of pull request from which dispatch is triggering. | ||
# | ||
# COMMENT_ID: ID of comment that is triggering a dispatch. | ||
# | ||
# DISPATCH_NAME: Name of a dispatch to be triggered. | ||
|
||
set -e | ||
|
||
if [ -z "$GITHUB_ACTIONS" ]; then | ||
echo "Must be run inside GitHub Actions CI" | ||
exit -1 | ||
fi | ||
|
||
if [ $# -ne 3 ]; then | ||
echo "Usage: $0 <PR_URL> <COMMENT_ID> <DISPATCH_NAME>" | ||
exit -1 | ||
fi | ||
|
||
pr_url=$1 | ||
comment_id=$2 | ||
dispatch_name=$3 | ||
|
||
pr=$( | ||
curl -sL \ | ||
-H "Accept: application/vnd.github.v3+json" \ | ||
-H "Authorization: token $SECRETS_WORKFLOW" \ | ||
"$pr_url" | ||
) | ||
data=$( | ||
jq -n \ | ||
--arg event_type "$dispatch_name" \ | ||
--arg pr_number "$(echo $pr | jq '.number')" \ | ||
--arg pr_sha "$(echo $pr | jq '.head.sha')" \ | ||
--arg pr_branch "$(echo $pr | jq '.head.ref')" \ | ||
--arg comment_number "$comment_id" \ | ||
'{"event_type":$event_type,"client_payload":{"pr_number":$pr_number,"pr_sha":$pr_sha,"pr_branch":$pr_branch,"comment_number":$comment_number}}' | ||
) | ||
curl -sL \ | ||
-X POST \ | ||
-H "Accept: application/vnd.github.v3+json" \ | ||
-H "Authorization: token $SECRETS_WORKFLOW" \ | ||
-d "$data" \ | ||
"${GITHUB_API_URL}/repos/microsoft/LightGBM/dispatches" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
name: Optional checks | ||
|
||
on: | ||
pull_request: | ||
branches: | ||
- master | ||
|
||
jobs: | ||
all-successful: | ||
timeout-minutes: 120 | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v2.3.4 | ||
with: | ||
fetch-depth: 5 | ||
submodules: false | ||
- name: Check that all tests succeeded | ||
run: | | ||
workflows=("R valgrind tests") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here we should add optional workflows which failures should block PR merging. |
||
for i in "${workflows[@]}"; do | ||
python "$GITHUB_WORKSPACE/.ci/get_workflow_status.py" "$i" \ | ||
|| { echo "The last reported status from workflow \"$i\" is failure. Commit fixes and rerun the workflow."; \ | ||
exit -1; } | ||
done |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,39 @@ | ||
name: R artifact builds | ||
|
||
on: | ||
pull_request_review_comment: | ||
types: [created] | ||
repository_dispatch: | ||
types: [gha_run_build_r_artifacts] | ||
|
||
jobs: | ||
cran-package: | ||
name: cran-package | ||
if: github.event.comment.body == '/gha build r-artifacts' && contains('OWNER,MEMBER,COLLABORATOR', github.event.comment.author_association) | ||
timeout-minutes: 60 | ||
runs-on: ubuntu-latest | ||
container: rocker/r-base | ||
env: | ||
SECRETS_WORKFLOW: ${{ secrets.WORKFLOW }} | ||
steps: | ||
- name: Install Git before checkout | ||
- name: Install essential software before checkout | ||
shell: bash | ||
run: | | ||
apt-get update | ||
apt-get install --no-install-recommends -y git | ||
apt-get install --no-install-recommends -y \ | ||
curl \ | ||
git \ | ||
jq | ||
- name: Checkout repository | ||
uses: actions/checkout@v2.3.4 | ||
with: | ||
fetch-depth: 5 | ||
submodules: true | ||
repository: microsoft/LightGBM | ||
ref: "refs/pull/${{ github.event.client_payload.pr_number }}/merge" | ||
- name: Send init status | ||
if: ${{ always() }} | ||
run: | | ||
$GITHUB_WORKSPACE/.ci/append_comment.sh \ | ||
"${{ github.event.client_payload.comment_number }}" \ | ||
"Workflow **${{ github.workflow }}** has been triggered! 🚀\r\n${GITHUB_SERVER_URL}/microsoft/LightGBM/actions/runs/${GITHUB_RUN_ID}" | ||
- name: Build package | ||
shell: bash | ||
id: build_package | ||
|
@@ -37,3 +49,9 @@ jobs: | |
with: | ||
name: ${{ steps.build_package.outputs.artifact_name }} | ||
path: ${{ steps.build_package.outputs.artifact_path }} | ||
- name: Send final status | ||
if: ${{ always() }} | ||
run: | | ||
$GITHUB_WORKSPACE/.ci/append_comment.sh \ | ||
"${{ github.event.client_payload.comment_number }}" \ | ||
"Status: ${{ job.status }}." | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I know that direct link for artifact downloading will be more useful. But unfortunately, it is impossible:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ha it's ok, we don't use this job that often and it's only one or two more clicks to get to the artifact |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why was this necessary? I have a lot more experience with
requests
thanurllib
, so I haven't seen this patternThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is for Python 2/3 compatibility. For the same reason I'm not using
with ...
statement but manually close connection. I know we dropped Python 2 support some time ago. But default installed Python version inubuntu-latest
container we run our actions in is 2. I really don't want to install conda/new Python version/additional packages because I want to keepOptional checks
workflow where this script is used as lightweight as possible with the aim of the fastest execution time. So this Python-agnostic and dependency-free solution will work fine on any default Python version that can be installed in container.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok I see, thanks. Fine with me