259 lines
9.9 KiB
YAML
259 lines
9.9 KiB
YAML
name: Blocked/Stacked Pull Requests Automation
|
|
|
|
on:
|
|
pull_request_target:
|
|
types:
|
|
- opened
|
|
- edited
|
|
workflow_dispatch:
|
|
inputs:
|
|
pr_id:
|
|
description: Local Pull Request number to work on
|
|
required: true
|
|
type: number
|
|
|
|
jobs:
|
|
blocked_status:
|
|
name: Check Blocked Status
|
|
runs-on: ubuntu-latest
|
|
|
|
permissions:
|
|
issues: write
|
|
pull-requests: write
|
|
statuses: write
|
|
checks: write
|
|
|
|
steps:
|
|
- name: Setup From Pull Request Event
|
|
if: github.event_name != 'workflow_dispatch'
|
|
id: pr_event_setup
|
|
env:
|
|
PR_JSON: "${{ toJSON(github.event.pull_request) }}"
|
|
run: |
|
|
# setup env for the rest of the workflow
|
|
{
|
|
echo "REPO=$(jq -r '.base.repo.name' <<< "$PR_JSON")"
|
|
echo "OWNER=$(jq -r '.base.repo.owner.login' <<< "$PR_JSON")"
|
|
echo "PR_NUMBER=$(jq -r '.number' <<< "$PR_JSON")"
|
|
echo "JOB_DATA=\"$(jq '
|
|
{
|
|
"repo": .base.repo.name,
|
|
"owner": .base.repo.owner.login,
|
|
"repoUrl": .base.repo.html_url,
|
|
"prNumber": .number,
|
|
"prHeadSha": .head.sha,
|
|
"prHeadLabel": .head.label,
|
|
"prBody": .body,
|
|
"prLabels": (reduce .labels[].name as $l ([]; . + [$l]))
|
|
}
|
|
' <<< "$PR_JSON")\""
|
|
} >> "$GITHUB_ENV"
|
|
cat $GITHUB_ENV
|
|
|
|
- name: Setup From Dispatch Event
|
|
if: github.event_name == 'workflow_dispatch'
|
|
id: dispatch_event_setup
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
OWNER_REPO: ${{ github.repository }}
|
|
OWNER: ${{ github.repository_owner }}
|
|
PR_NUMBER: ${{ inputs.pr_id }}
|
|
run: |
|
|
# setup env for the rest of the workflow
|
|
owner_prefix="$OWNER/"
|
|
REPO="${OWNER_REPO#"$owner_prefix"}"
|
|
PR_JSON=$(
|
|
gh api \
|
|
-H "Accept: application/vnd.github.raw+json" \
|
|
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
"/repos/$OWNER/$REPO/pulls/$PR_NUMBER"
|
|
)
|
|
{
|
|
echo "REPO=$(jq -r '.base.repo.name' <<< "$PR_JSON")"
|
|
echo "OWNER=$(jq -r '.base.repo.owner.login' <<< "$PR_JSON")"
|
|
echo "PR_NUMBER=$(jq -r '.number' <<< "$PR_JSON")"
|
|
echo "JOB_DATA=\"$(jq '
|
|
{
|
|
"repo": .base.repo.name,
|
|
"owner": .base.repo.owner.login,
|
|
"repoUrl": .base.repo.html_url,
|
|
"prNumber": .number,
|
|
"prHeadSha": .head.sha,
|
|
"prHeadLabel": .head.label,
|
|
"prBody": .body,
|
|
"prLabels": (reduce .labels[].name as $l ([]; . + [$l]))
|
|
}
|
|
' <<< "$PR_JSON")\""
|
|
} >> "$GITHUB_ENV"
|
|
cat $GITHUB_ENV
|
|
|
|
|
|
- name: Find Blocked/Stacked PRs in body
|
|
id: pr_ids
|
|
run: |
|
|
PRS=$(
|
|
jq '
|
|
.prBody as $body
|
|
| (
|
|
$body |
|
|
reduce (
|
|
. | scan("blocked (?:by|on):? #([0-9]+)")
|
|
| map({
|
|
"type": "Blocked on",
|
|
"number": ( . | tonumber )
|
|
})
|
|
) as $i ([]; . + [$i])
|
|
) as $bprs
|
|
| (
|
|
$body |
|
|
reduce (
|
|
. | scan("stacked on:? #([0-9]+)")
|
|
| map({
|
|
"type": "Stacked on",
|
|
"number": ( . | tonumber )
|
|
})
|
|
) as $i ([]; . + [$i])
|
|
) as $sprs
|
|
| ($bprs + $sprs) as $prs
|
|
| {
|
|
"blocking": $prs,
|
|
"numBlocking": ( $prs | length),
|
|
}
|
|
' <<< "$JOB_DATA"
|
|
)
|
|
echo "prs=$PRS" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Collect Blocked PR Data
|
|
id: blocked_data
|
|
if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
BLOCKED_PR_DATA=$(
|
|
while read -r pr_data ; do
|
|
gh api \
|
|
-H "Accept: application/vnd.github+json" \
|
|
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
"/repos/$OWNER/$REPO/pulls/$(jq -r '.number' <<< "$pr_data")" \
|
|
| jq --arg type "$(jq -r '.type' <<< "$pr_data")" \
|
|
'
|
|
. | {
|
|
"type": $type,
|
|
"number": .number,
|
|
"merged": .merged,
|
|
"labels": (reduce .labels[].name as $l ([]; . + [$l])),
|
|
"basePrUrl": .html_url,
|
|
"baseRepoName": .head.repo.name,
|
|
"baseRepoOwner": .head.repo.owner.login,
|
|
"baseRepoUrl": .head.repo.html_url,
|
|
"baseSha": .head.sha,
|
|
"baseRefName": .head.ref,
|
|
}
|
|
'
|
|
done < <(jq -c '.blocking[]' <<< "${{steps.pr_ids.outputs.prs}}") | jq -s
|
|
)
|
|
echo "state=$BLOCKED_PR_DATA" >> "$GITHUB_OUTPUT"
|
|
echo "all_merged=$(jq 'all(.[].merged; .)' <<< "$BLOCKED_PR_DATA")"
|
|
|
|
- name: Apply Blocked Label if Missing
|
|
id: label_blocked
|
|
if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 && !contains(fromJSON(env.PR_LABELS), 'blocked') && !fromJSON(steps.blocked_data.outputs.all_merged)
|
|
continue-on-error: true
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
gh api \
|
|
--method POST \
|
|
-H "Accept: application/vnd.github+json" \
|
|
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
"/repos/$OWNER/$REPO/issues/$PR_NUMBER/labels" \
|
|
-f "labels[]=blocked"
|
|
|
|
- name: Remove 'blocked' Label if All Dependencies Are Merged
|
|
id: unlabel_blocked
|
|
if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 && fromJSON(steps.blocked_data.outputs.all_merged)
|
|
continue-on-error: true
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
gh api \
|
|
--method DELETE \
|
|
-H "Accept: application/vnd.github+json" \
|
|
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
"/repos/$OWNER/$REPO/issues/$PR_NUMBER/labels/blocked"
|
|
|
|
- name: Apply 'blocking' Label to Dependencies if Missing
|
|
id: label_blocking
|
|
if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0
|
|
continue-on-error: true
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
# label pr dependencies with 'blocking' if not already
|
|
while read -r pr_data ; do
|
|
if jq -e 'all(.labels[]; . != "blocking")' <<< "$pr_data" > /dev/null ; then
|
|
pr=$(jq -r '.number' <<< "$pr_data")
|
|
gh api \
|
|
--method POST \
|
|
-H "Accept: application/vnd.github+json" \
|
|
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
"/repos/$OWNER/$REPO/issues/$pr/labels" \
|
|
-f "labels[]=blocking"
|
|
fi
|
|
done < <(jq -c '.[]' <<< "${{steps.blocked_data.outputs.state}}")
|
|
|
|
- name: Apply Blocking PR Status Check
|
|
id: blocked_check
|
|
if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0
|
|
continue-on-error: true
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
pr_head_sha=$(jq -r '.prHeadSha' <<< "$JOB_DATA")
|
|
# create commit Status, overwrites previous identical context
|
|
while read -r pr_data ; do
|
|
DESC=$(
|
|
jq -r ' "Blocking PR #" + (.number | tostring) + " is " + (if .merged then "" else "not yet " end) + "merged"' <<< "$pr_data"
|
|
)
|
|
gh api \
|
|
--method POST \
|
|
-H "Accept: application/vnd.github+json" \
|
|
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
"/repos/${OWNER}/${REPO}/statuses/${pr_head_sha}" \
|
|
-f "state=$(jq -r 'if .merged then "success" else "failure" end' <<< "$pr_data")" \
|
|
-f "target_url=$(jq -r '.basePrUrl' <<< "$pr_data" )" \
|
|
-f "description=$DESC" \
|
|
-f "context=continuous-integration/blocked-pr-check:$(jq '.number' <<< "$pr_data")"
|
|
done < <(jq -c '.[]' <<< "${{steps.blocked_data.outputs.state}}")
|
|
|
|
- name: Context Comment
|
|
id: blocked_comment
|
|
if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0
|
|
continue-on-error: true
|
|
run: |
|
|
COMMENT_PATH="$(pwd)/temp_comment_file.txt"
|
|
touch "$COMMENT_PATH"
|
|
echo "" > "$COMMENT_PATH"
|
|
pr_head_label=$(jq -r '.prHeadLabel' <<< "$JOB_DATA")
|
|
while read -r pr_data ; do
|
|
base_pr=$(jq -r '.number' <<< "$pr_data")
|
|
base_ref_name=$(jq -r '.baseRefName' <<< "$pr_data")
|
|
base_repo_owner=$(jq -r '.baseRepoOwner' <<< "$pr_data")
|
|
base_repo_name=$(jq -r '.baseRepoName' <<< "$pr_data")
|
|
compare_url="https://github.com/$base_repo_owner/$base_repo_name/compare/$base_ref_name...$pr_head_label"
|
|
status=$(jq 'if .merged then ":heavy_check_mark: Merged" else ":x: Not Merged" end' <<< "$pr_data")
|
|
type=$(jq -r '.type' <<< "$pr_data")
|
|
echo " - $type #$base_pr $status [(compare)]($compare_url)" >> "$COMMENT_PATH"
|
|
done < <(jq -c '.[]' <<< "${{steps.blocked_data.outputs.state}}")
|
|
echo "file_path=${COMMENT_PATH}" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: 💬 PR Comment
|
|
if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0
|
|
continue-on-error: true
|
|
uses: spicyparrot/pr-comment-action@v1.0.0
|
|
with:
|
|
comment: "### PR Dependencies :pushpin:"
|
|
comment_path: ${{ steps.blocked_comment.outputs.file_path }}
|
|
comment_id: "block_pr_dependencies"
|
|
|