-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve Asana integration for failed PR checks (#514)
Task/Issue URL: https://app.asana.com/0/1203301625297703/1205530774734473/f iOS PR: duckduckgo/iOS#2036 macOS PR: duckduckgo/macos-browser#1673 What kind of version bump will this require?: N/A CC: @bwaresiak Description: This patch adds a standalone GitHub Action called asana-pr-failed-checks to report PR checks runs failures to Asana. This action is reused by iOS and macOS repository without code duplication. The action has 2 modes: create task and close task. First is run when a PR checks run fails (only after merging the PR to the default branch, as asserted in the PR checks workflow definition). It creates a task in Asana and populates "Workflow ID" custom field with the PR checks workflow run ID. If that run is retried and passes (e.g. because of a flaky test), the action is called in "close task" mode. It then searches relevant Asana project section for a task with the given workflow ID and closes the task. The action maps GitHub user handles to Asana user IDs and based on the map provided in the JSON file.
- Loading branch information
Showing
4 changed files
with
308 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
name: Report Failed PR Checks to Asana | ||
description: Integrates with Asana to report failed PR checks as a task. | ||
inputs: | ||
asana-access-token: | ||
description: "Asana access token" | ||
required: true | ||
type: string | ||
asana-section-id: | ||
description: "Asana project's section ID" | ||
required: true | ||
type: string | ||
action: | ||
description: "Action to perform: choose between 'create-task' and 'close-task'" | ||
required: true | ||
type: string | ||
commit-author: | ||
description: "Last commit author's GitHub handle" | ||
required: false | ||
type: string | ||
runs: | ||
using: "composite" | ||
steps: | ||
- env: | ||
WORKFLOW_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} | ||
GITHUB_REF_NAME: ${{ github.ref_name }} | ||
GITHUB_RUN_ID: ${{ github.run_id }} | ||
ASANA_SECTION_ID: ${{ inputs.asana-section-id }} | ||
ASANA_ACCESS_TOKEN: ${{ inputs.asana-access-token }} | ||
shell: bash | ||
run: | | ||
case "${{ inputs.action }}" in | ||
"create-task") | ||
if [[ -n "${{ inputs.commit-author }}" ]]; then | ||
export ASANA_ASSIGNEE=$(jq -r .${{ inputs.commit-author }} < ${{ github.action_path }}/user_ids.json) | ||
fi | ||
${{ github.action_path }}/report-failed-pr-checks.sh create-task \ | ||
-t "PR Check is failing on ${{ env.GITHUB_REF_NAME }}" \ | ||
-d "PR Checks conducted after merging have failed. See ${{ env.WORKFLOW_URL }}. Follow the steps on https://app.asana.com/0/1202500774821704/1205317064731691 to resolve this issue." | ||
;; | ||
"close-task") | ||
${{ github.action_path }}/report-failed-pr-checks.sh close-task \ | ||
-m "Closing this one as checks are passing after a re-run. See ${{ env.WORKFLOW_URL }}/attempts/${{ github.run_attempt }} for details." | ||
;; | ||
*) | ||
echo "::error::Invalid action '${{ inputs.action }}'." | ||
exit 1 | ||
;; | ||
esac |
192 changes: 192 additions & 0 deletions
192
.github/actions/asana-failed-pr-checks/report-failed-pr-checks.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
#!/bin/bash | ||
|
||
set -eo pipefail | ||
|
||
workspace_id="137249556945" | ||
project_id="1205237866452338" | ||
workflow_id_custom_field_id="1205563320492190" | ||
asana_api_url="https://app.asana.com/api/1.0" | ||
|
||
print_usage_and_exit() { | ||
local reason=$1 | ||
|
||
cat <<- EOF | ||
Usage: | ||
$ $(basename "$0") <create-task|close-task> [-h] [-t <title>] [-d <description>] [-m <closing_comment_message>] | ||
Actions: | ||
create-task Create a new task in Asana | ||
close-task Close an existing Asana task for the workflow ID provided in environment variable WORKFLOW_ID | ||
Options (only used for create-task): | ||
-t <title> Asana task title | ||
-d <description> Asana task description | ||
-m <closing_comment_message> Closing comment message | ||
Note: This script is intended for CI use only. You shouldn't call it directly. | ||
EOF | ||
|
||
echo "${reason}" | ||
exit 1 | ||
} | ||
|
||
read_command_line_arguments() { | ||
action="$1" | ||
case "${action}" in | ||
create-task) | ||
;; | ||
close-task) | ||
;; | ||
*) | ||
print_usage_and_exit "Unknown action '${action}'" | ||
;; | ||
esac | ||
|
||
shift 1 | ||
|
||
case "${action}" in | ||
create-task) | ||
if (( $# < 2 )); then | ||
print_usage_and_exit "Missing arguments" | ||
fi | ||
;; | ||
close-task) | ||
if (( $# < 1 )); then | ||
print_usage_and_exit "Missing message argument" | ||
fi | ||
;; | ||
esac | ||
|
||
while getopts 'd:hm:t:' OPTION; do | ||
case "${OPTION}" in | ||
d) | ||
description="${OPTARG}" | ||
;; | ||
h) | ||
print_usage_and_exit | ||
;; | ||
m) | ||
message="${OPTARG}" | ||
;; | ||
t) | ||
title="${OPTARG}" | ||
;; | ||
*) | ||
print_usage_and_exit "Unknown option '${OPTION}'" | ||
;; | ||
esac | ||
done | ||
|
||
shift $((OPTIND-1)) | ||
} | ||
|
||
create_task() { | ||
local task_name=$1 | ||
local description | ||
local task_id | ||
local assignee_param | ||
description=$(sed -E -e 's/\\/\\\\/g' -e 's/"/\\"/g' <<< "$2") | ||
if [[ -n "${assignee}" ]]; then | ||
assignee_param="\"${assignee}\"" | ||
else | ||
assignee_param="null" | ||
fi | ||
|
||
task_id=$(curl -X POST -s "${asana_api_url}/tasks?opt_fields=gid" \ | ||
-H "Authorization: Bearer ${asana_personal_access_token}" \ | ||
-H 'content-type: application/json' \ | ||
-d "{ | ||
\"data\": { | ||
\"assignee\": ${assignee_param}, | ||
\"name\": \"${task_name}\", | ||
\"resource_subtype\": \"default_task\", | ||
\"notes\": \"${description}\", | ||
\"projects\": [ | ||
\"${project_id}\" | ||
], | ||
\"custom_fields\": { | ||
\"${workflow_id_custom_field_id}\": \"${workflow_id}\" | ||
} | ||
} | ||
}" \ | ||
| jq -r '.data.gid') | ||
|
||
return_code="$(curl -X POST -s "${asana_api_url}/sections/${section_id}/addTask" \ | ||
-H "Authorization: Bearer ${asana_personal_access_token}" \ | ||
-H 'content-type: application/json' \ | ||
--write-out '%{http_code}' \ | ||
--output /dev/null \ | ||
-d "{\"data\": {\"task\": \"${task_id}\"}}")" | ||
|
||
[[ ${return_code} -eq 200 ]] | ||
} | ||
|
||
find_task_for_workflow_id() { | ||
local workflow_id=$1 | ||
curl -s "${asana_api_url}/workspaces/${workspace_id}/tasks/search?opt_fields=gid&resource_subtype=default_task&projects.any=${project_id}&limit=1&custom_fields.${workflow_id_custom_field_id}.value=${workflow_id}" \ | ||
-H "Authorization: Bearer ${asana_personal_access_token}" \ | ||
| jq -r "if (.data | length) != 0 then .data[0].gid else empty end" | ||
} | ||
|
||
add_comment_to_task() { | ||
local task_id=$1 | ||
local message | ||
local return_code | ||
message=$(sed -E -e 's/\\/\\\\/g' -e 's/"/\\"/g' <<< "$2") | ||
|
||
return_code="$(curl -X POST -s "${asana_api_url}/tasks/${task_id}/stories" \ | ||
-H "Authorization: Bearer ${asana_personal_access_token}" \ | ||
-H 'content-type: application/json' \ | ||
--write-out '%{http_code}' \ | ||
--output /dev/null \ | ||
-d "{\"data\": {\"text\": \"${message}\"}}")" | ||
|
||
[[ ${return_code} -eq 201 ]] | ||
} | ||
|
||
close_task() { | ||
local task_id=$1 | ||
local return_code | ||
|
||
return_code="$(curl -X PUT -s "${asana_api_url}/tasks/${task_id}" \ | ||
-H "Authorization: Bearer ${asana_personal_access_token}" \ | ||
-H 'content-type: application/json' \ | ||
--write-out '%{http_code}' \ | ||
--output /dev/null \ | ||
-d "{\"data\": {\"completed\": true}}")" | ||
|
||
[[ ${return_code} -eq 200 ]] | ||
} | ||
|
||
main() { | ||
local asana_personal_access_token="${ASANA_ACCESS_TOKEN}" | ||
local section_id="${ASANA_SECTION_ID}" | ||
local assignee="${ASANA_ASSIGNEE}" | ||
local workflow_id="${GITHUB_RUN_ID}" | ||
local action | ||
local title | ||
local description | ||
local message | ||
|
||
read_command_line_arguments "$@" | ||
|
||
case "${action}" in | ||
create-task) | ||
create_task "${title}" "${description}" | ||
;; | ||
close-task) | ||
task_id=$(find_task_for_workflow_id "${workflow_id}") | ||
if [[ -n "${task_id}" ]]; then | ||
add_comment_to_task "${task_id}" "${message}" | ||
close_task "${task_id}" | ||
else | ||
echo "No task found for workflow ID '${workflow_id}'" | ||
fi | ||
;; | ||
*) | ||
print_usage_and_exit "Unknown action '${action}'" | ||
;; | ||
esac | ||
} | ||
|
||
main "$@" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"afterxleep": "1204051006248670", | ||
"amddg44": "1201462886802326", | ||
"ayoy": "1201621708108816", | ||
"brindy": "392891325557408", | ||
"bwaresiak": "856498667309057", | ||
"Bunn": "1201011656765575", | ||
"dharb": "246491496396030", | ||
"diegoreymendez": "1203108348814444", | ||
"GioSensation": "1144596633302696", | ||
"graeme": "1202926619868495", | ||
"jaceklyp": "1201392122286920", | ||
"jonathanKingston": "1199237043596759", | ||
"jotaemepereira": "1203972458584425", | ||
"ladamski": "1171671347221380", | ||
"mallexxx": "1202406491309505", | ||
"miasma13": "1200848783436158", | ||
"muodov": "1201807839802870", | ||
"samsymons": "1193060753408019", | ||
"shakyShane": "1201141132927824", | ||
"SabrinaTardio": "1204024359086954", | ||
"THISISDINOSAUR": "1187352150811184", | ||
"tomasstrba": "1148564399176232", | ||
"viktorjansson": "970019368055267", | ||
"vinay-nadig-0042": "1202096681833210" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters