-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move GitHub custom actions from apple-infra (#6)
- Loading branch information
1 parent
ab53ca4
commit 731e681
Showing
5 changed files
with
262 additions
and
0 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,11 @@ | ||
// swift-tools-version:5.7 | ||
|
||
// Leave blank. This is only here so that Xcode doesn't display it. | ||
|
||
import PackageDescription | ||
|
||
let package = Package( | ||
name: "actions", | ||
products: [], | ||
targets: [] | ||
) |
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,45 @@ | ||
name: Create Asana PR subtask | ||
description: | | ||
Creates a PR subtask and assigns it to the team member in charge of the GitHub review. | ||
If the PR subtask already exists, it ensures the reviewer is assigned and the task is not marked 'completed'. | ||
inputs: | ||
access-token: | ||
description: "Asana access token" | ||
required: true | ||
type: string | ||
asana-task-id: | ||
description: "Asana Task ID for the PR" | ||
required: string | ||
type: string | ||
github-reviewer-user: | ||
description: "GitHub username for the requested reviewer" | ||
required: true | ||
type: string | ||
runs: | ||
using: "composite" | ||
steps: | ||
- name: Get Asana user ID | ||
id: get-asana-user-id | ||
uses: duckduckgo/apple-infra/actions/asana-get-user-id-for-github-handle@main | ||
with: | ||
access-token: ${{ inputs.access-token }} | ||
github-handle: ${{ inputs.github-reviewer-user }} | ||
|
||
- name: Get PR URL | ||
id: get-pr-url | ||
shell: bash | ||
run: | | ||
echo "url=https://github.com/${{ github.repository }}/pull/${{ github.event.number }}" >> "$GITHUB_OUTPUT" | ||
- name: "Create PR Subtask" | ||
shell: bash | ||
env: | ||
ASANA_ACCESS_TOKEN: ${{ inputs.access-token }} | ||
run: | | ||
# If the Asana User ID is not found, exit with an error | ||
if [[ -z "${{ steps.get-asana-user-id.outputs.user-id }}" ]]; then | ||
echo "Error: Failed to get Asana user ID for GitHub user ${{ inputs.github-reviewer-user }}" | ||
exit 1 | ||
else | ||
${{ github.action_path }}/asana-create-pr-subtask.sh ${{ inputs.asana-task-id }} ${{ steps.get-asana-user-id.outputs.user-id }} ${{ steps.get-pr-url.outputs.url }} | ||
fi |
149 changes: 149 additions & 0 deletions
149
actions/asana-create-pr-subtask/asana-create-pr-subtask.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,149 @@ | ||
#!/bin/bash | ||
# | ||
# This script creates a PR subtask and assign it to a team member when a review is requested on GitHub | ||
# | ||
|
||
set -e -o pipefail | ||
|
||
asana_api_url="https://app.asana.com/api/1.0" | ||
pr_prefix="PR:" | ||
|
||
# Fetch the subtasks of a task with the given Asana task ID. | ||
_fetch_subtasks() { | ||
local asana_task_id="$1" | ||
local url="${asana_api_url}/tasks/${asana_task_id}/subtasks?opt_fields=name,completed,parent.name,assignee" | ||
|
||
local response | ||
response="$(curl -fLSs "$url" -H "Authorization: Bearer ${ASANA_ACCESS_TOKEN}")" | ||
|
||
# extract the task id, task name, task completed status, assignee id and parent name | ||
jq -c '[ | ||
.data[] | { | ||
task_id: .gid, | ||
task_name: .name, | ||
task_completed: .completed, | ||
assignee: .assignee.gid, | ||
parent_name: .parent.name | ||
} | ||
]' <<< "$response" | ||
} | ||
|
||
# Sets the parent task name | ||
_set_parent_task_name() { | ||
local subtasks="$1" | ||
local asana_task_id="$2" | ||
|
||
# extracts the parent name from the first object | ||
parent_task_name=$(echo "$subtasks" | jq -r '.[0].parent_name') | ||
|
||
# if parent_task_name is nil it means that there are no subtask in the current task so we fetch the parent name | ||
if [ -z "$parent_task_name" ] || [ "$parent_task_name" = "null" ]; then | ||
local url="${asana_api_url}/tasks/${asana_task_id}" | ||
parent_task_name="$(curl -fLSs "$url" -H "Authorization: Bearer ${ASANA_ACCESS_TOKEN}" | jq -r '.data.name')" | ||
fi | ||
} | ||
|
||
# Checks if a subtask for the PR already exists. | ||
_check_pr_subtask_exist() { | ||
local response="$1" | ||
local asana_assignee_id="$2" | ||
|
||
# read each line of the array | ||
# extract the task name and trim leading and trailing white spaces | ||
# extract the parent name and trim leading and trailing white spaces | ||
# extract the assignee name | ||
# checks if the task name has 'PR:' prefix and if contains the parent name and if it's assigned to the reviewer | ||
echo "$response" | jq -c '.[]' | while read -r item; do | ||
task_name=$(jq -r '.task_name' <<< "$item" | awk '{$1=$1};1') | ||
parent_name=$(jq -r '.parent_name' <<< "$item" | awk '{$1=$1};1') | ||
assignee=$(jq -r '.assignee' <<< "$item") | ||
|
||
if [[ "$task_name" == "${pr_prefix}"* && "$task_name" == *"$parent_name"* && "$assignee" == "$asana_assignee_id" ]]; then | ||
echo "$item" | ||
fi | ||
done | ||
|
||
} | ||
|
||
# Creates a subtask called PR: ${task_title}, set the PR URL as description and assign to the requested reviewer | ||
_create_pr_subtask() { | ||
local asana_task_id="$1" | ||
local asana_assignee_id="$2" | ||
local github_pr_url="$3" | ||
local url="${asana_api_url}/tasks/${asana_task_id}/subtasks?opt_fields=gid" | ||
|
||
local payload | ||
payload=$(cat <<-EOF | ||
{ | ||
"data": { | ||
"assignee": "${asana_assignee_id}", | ||
"notes": "${pr_prefix} ${github_pr_url}", | ||
"name": "${pr_prefix} ${parent_task_name}" | ||
} | ||
} | ||
EOF | ||
) | ||
|
||
_execute_create_or_update_asana_task_request POST "$url" "$payload" | ||
} | ||
|
||
# Assigns a reviewer to the existing PR subtask and update the task status if it is marked 'completed' | ||
_mark_task_uncompleted_if_needed() { | ||
local pr_subtask="$1" | ||
|
||
# get the task id | ||
local task_id | ||
task_id=$(echo "$pr_subtask" | jq -r '.task_id') | ||
# get the completed status | ||
local task_status_completed | ||
task_status_completed=$(echo "$pr_subtask" | jq -r '.task_completed') | ||
|
||
# if the status is completed mark the task uncompleted. | ||
if [ "$task_status_completed" = true ]; then | ||
local url="${asana_api_url}/tasks/${task_id}?opt_fields=gid" | ||
local payload='{"data":{"completed":false}}' | ||
_execute_create_or_update_asana_task_request PUT "$url" "$payload" | ||
fi | ||
} | ||
|
||
# Executes an Asana request to create or update a Subtask | ||
_execute_create_or_update_asana_task_request() { | ||
local method="$1" | ||
local url="$2" | ||
local payload="$3" | ||
|
||
local task_id | ||
task_id="$(curl -fLSs -X "$method" "$url" \ | ||
-H "Authorization: Bearer ${ASANA_ACCESS_TOKEN}" \ | ||
-H 'accept: application/json' \ | ||
-H 'content-type: application/json' \ | ||
--data "${payload}" \ | ||
| jq -r .data.gid)" | ||
} | ||
|
||
main() { | ||
local asana_task_id="$1" | ||
local asana_assignee_id="$2" | ||
local github_pr_url="$3" | ||
|
||
# fetch the task subtasks | ||
local subtasks | ||
subtasks=$(_fetch_subtasks "$asana_task_id") | ||
|
||
# set the parent task name | ||
_set_parent_task_name "$subtasks" "$asana_task_id" | ||
|
||
# check if the PR subtask already exist | ||
local pr_subtask | ||
pr_subtask=$(_check_pr_subtask_exist "$subtasks" "$asana_assignee_id") | ||
|
||
# if the PR subtask exist, mark the task uncompleted if it the task is marked completed | ||
# otherwise, create the PR subtask and assign it to the reviewer | ||
if [[ -n "$pr_subtask" ]]; then | ||
_mark_task_uncompleted_if_needed "$pr_subtask" | ||
else | ||
_create_pr_subtask "$asana_task_id" "$asana_assignee_id" "$github_pr_url" | ||
fi | ||
} | ||
|
||
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,27 @@ | ||
name: Get Asana user ID matching GitHub handle | ||
description: Returns Asana user ID that matches GitHub user handle | ||
inputs: | ||
access-token: | ||
description: "Asana access token" | ||
required: true | ||
type: string | ||
github-handle: | ||
description: "GitHub user handle" | ||
required: true | ||
type: string | ||
outputs: | ||
user-id: | ||
description: "Asana user ID" | ||
value: ${{ steps.get-asana-user-id.outputs.user-id }} | ||
runs: | ||
using: "composite" | ||
steps: | ||
- id: get-asana-user-id | ||
run: | | ||
user_id="$(yq ."${{ inputs.github-handle }}" ${{ github.action_path }}/github-asana-user-id-mapping.yml)" | ||
if [[ -z "$user_id" || "$user_id" == "null" ]]; then | ||
echo "::warning::Asana User ID not found for GitHub handle: ${{ inputs.github-handle }}" | ||
else | ||
echo "user-id=${user_id}" >> $GITHUB_OUTPUT | ||
fi | ||
shell: bash |
30 changes: 30 additions & 0 deletions
30
actions/asana-get-user-id-for-github-handle/github-asana-user-id-mapping.yml
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,30 @@ | ||
aataraxiaa: "1206488453854239" | ||
afterxleep: "1204051006248664" | ||
alessandroboron: "1206329551987270" | ||
amddg44: "1201462886797771" | ||
ayoy: "1201621708091911" | ||
brindy: "33604954490307" | ||
Bunn: "1201011656757987" | ||
bwaresiak: "856498666990313" | ||
dharb: "246491496396026" | ||
diegoreymendez: "1203108348749442" | ||
dus7: "1206226850447383" | ||
federicocappelli: "1205736672764360" | ||
GioSensation: "1144596632862977" | ||
graeme: "1202926619863343" | ||
jaceklyp: "1201392122268514" | ||
jonathanKingston: "1199237043579003" | ||
jotaemepereira: "1203972458584419" | ||
ladamski: "1171671023737946" | ||
loremattei: "1202204871961729" | ||
mallexxx: "1202406491309495" | ||
miasma13: "1200848783415037" | ||
muodov: "1201807839780469" | ||
quanganhdo: "1205591970852428" | ||
SabrinaTardio: "1204024359086948" | ||
samsymons: "1193060753332998" | ||
shakyShane: "1201141132903155" | ||
THISISDINOSAUR: "1187352150204278" | ||
tomasstrba: "1148564398679340" | ||
viktorjansson: "970019367293722" | ||
vinay-nadig-0042: "1202096681806170" |