From a65ced1598751d5838859a7c9a7b15b85ab310c2 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Tue, 11 Mar 2025 17:01:58 -0700 Subject: [PATCH 01/27] ci: add a workflow to detect and check dependencies for blocked pull requests Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/blocked_prs.yml | 236 ++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 .github/workflows/blocked_prs.yml diff --git a/.github/workflows/blocked_prs.yml b/.github/workflows/blocked_prs.yml new file mode 100644 index 000000000..8f060dbb6 --- /dev/null +++ b/.github/workflows/blocked_prs.yml @@ -0,0 +1,236 @@ +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: + block_status: + name: Check Blocked Status + runs-on: ubuntu-latest + steps: + - name: Setup From Pull Request Vent + if: ${{ github.event_name != 'workflow_dispatch' }} + id: pr_event_setup + env: + REPO_L: ${{ github.event.pull_request.base.repo.name }} + OWNER_L: ${{ github.event.pull_request.base.repo.owner.login }} + REPO_URL_L: $ {{ github.event.pull_request.base.repo.html_url }} + PR_HEAD_SHA_L: ${{ github.event.pull_request.head.sha }} + PR_NUMBER_L: ${{ github.event.pull_request.number }} + PR_HEAD_LABEL_L: ${{ github.event.pull_request.head.label }} + PR_BODY_L: ${{ github.event.pull_request.body }} + PR_LABLES_L: ${{ github.event.pull_request.labels }} + run: | + # setup env for the rest of the workflow + { + echo "REPO=$REPO_L" + echo "OWNER=$OWNER_L" + echo "REPO_URL=$REPO_URL_L" + echo "PR_NUMBER=$PR_NUMBER_L" + echo "PR_HEAD_SHA=$PR_HEAD_SHA_L" + echo "PR_HEAD_LABEL=$PR_HEAD_LABEL_L" + echo "PR_BODY=$PR_BODY_L" + echo "PR_LABELS=$(jq 'reduce .[].name as $l ([]; . + [$l])' <<< "$PR_LABELS_L" )" + } >> "$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_L: ${{ github.repository }} + OWNER_L: ${{ github.repository_owner }} + REPO_URL_L: $ {{ github.repositoryUrl }} + PR_NUMBER_L: ${{ inputs.pr_id }} + run: | + # setup env for the rest of the workflow + owner_prefix="$OWNER_L/" + REPO_L="${OWNER_REPO_L#"$owner_prefix"}" + PR_L=$( + gh api \ + -H "Accept: application/vnd.github.raw+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "/repos/$OWNER_L/$REPO_L/pulls/$PR_NUMBER_L" + ) + PR_HEAD_SHA_L=$(jq -r '.head.sha' <<< "$PR_L") + PR_HEAD_LABEL_L=$(jq -r '.head.label' <<< "$PR_L") + PR_BODY_L=$(jq -r '.body' <<< "$PR_L") + PR_LABELS_L=$(jq '.labels' <<< "$PR_L") + { + echo "REPO=$REPO_L" + echo "OWNER=$OWNER_L" + echo "REPO_URL=$REPO_URL_L" + echo "PR_NUMBER=$PR_NUMBER_L" + echo "PR_HEAD_SHA=$PR_HEAD_SHA_L" + echo "PR_HEAD_LABEL=$PR_HEAD_LABEL_L" + echo "PR_BODY=$PR_BODY_L" + echo "PR_LABELS=$(jq 'reduce .[].name as $l ([]; . + [$l])' <<< "$PR_LABELS_L" )" + } >> "$GITHUB_ENV" + + + - name: Find Blocked/Stacked PRs in body + id: pr_ids + run: | + PRS=$( + jq ' + . as $body + | ( + $body | scan("blocked (?by)|(?on):? #(?[0-9]+)") + | map({ + "type": "Blocked on", + "number": ( . | tonumber ) + }) + ) as $bprs + | ( + $body | scan("stacked on:? #(?[0-9]+)") + | map({ + "type": "Stacked on", + "number": ( . | tonumber ) + }) + ) as $sprs + | ($bprs + $sprs) as $prs + | { + "blocking": $prs, + "numBlocking": ( $prs | length), + } + ' <<< "$PR_BODY" + ) + echo "prs=$PRS" >> "$GITHUB_OUPUT" + + - 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 ; do + gh api \ + -H "Accept: application/vnd.github.raw+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "/repos/$OWNER/$REPO/pulls/$(jq -r '.number' <<< "$PR")" \ + | jq --arg type "$(jq -r '.type' <<< "$PR")" \ + ' + . | { + "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_OUPUT" + 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: | + # 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" + while read -r PR_DATA ; do + BASE_PR=$(jq '.number' <<< "$PR_DATA") + BASE_REF_NAME=$(jq '.baseRefName' <<< "$PR_DATA") + COMPARE_URL="$REPO_URL/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" From be1ce8dd9d0612559560a03c4e70f667430de939 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Tue, 11 Mar 2025 20:19:03 -0700 Subject: [PATCH 02/27] ci: add workflow to trigger refresh of dependants when a blocking pr is merged Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/blocked_prs.yml | 40 ++++++++----- .github/workflows/merge_blocking_pr.yml | 80 +++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/merge_blocking_pr.yml diff --git a/.github/workflows/blocked_prs.yml b/.github/workflows/blocked_prs.yml index 8f060dbb6..ecafc65a8 100644 --- a/.github/workflows/blocked_prs.yml +++ b/.github/workflows/blocked_prs.yml @@ -13,12 +13,19 @@ on: type: number jobs: - block_status: + 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 Vent - if: ${{ github.event_name != 'workflow_dispatch' }} + if: github.event_name != 'workflow_dispatch' id: pr_event_setup env: REPO_L: ${{ github.event.pull_request.base.repo.name }} @@ -43,7 +50,7 @@ jobs: } >> "$GITHUB_ENV" - name: Setup From Dispatch Event - if: ${{ github.event_name == 'workflow_dispatch' }} + if: github.event_name == 'workflow_dispatch' id: dispatch_event_setup env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -57,13 +64,13 @@ jobs: REPO_L="${OWNER_REPO_L#"$owner_prefix"}" PR_L=$( gh api \ - -H "Accept: application/vnd.github.raw+json" \ + -H "Accept: application/vnd.github.text+json" \ -H "X-GitHub-Api-Version: 2022-11-28" \ "/repos/$OWNER_L/$REPO_L/pulls/$PR_NUMBER_L" ) PR_HEAD_SHA_L=$(jq -r '.head.sha' <<< "$PR_L") PR_HEAD_LABEL_L=$(jq -r '.head.label' <<< "$PR_L") - PR_BODY_L=$(jq -r '.body' <<< "$PR_L") + PR_BODY_L=$(jq -r '.body_text' <<< "$PR_L") PR_LABELS_L=$(jq '.labels' <<< "$PR_L") { echo "REPO=$REPO_L" @@ -84,7 +91,7 @@ jobs: jq ' . as $body | ( - $body | scan("blocked (?by)|(?on):? #(?[0-9]+)") + $body | scan("blocked (?(?by)|(?on)):? #(?[0-9]+)") | map({ "type": "Blocked on", "number": ( . | tonumber ) @@ -104,18 +111,18 @@ jobs: } ' <<< "$PR_BODY" ) - echo "prs=$PRS" >> "$GITHUB_OUPUT" + echo "prs=$PRS" >> "$GITHUB_OUTPUT" - name: Collect Blocked PR Data id: blocked_data - if: ${{ fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 }} + if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | BLOCKED_PR_DATA=$( while read -r PR ; do gh api \ - -H "Accept: application/vnd.github.raw+json" \ + -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2022-11-28" \ "/repos/$OWNER/$REPO/pulls/$(jq -r '.number' <<< "$PR")" \ | jq --arg type "$(jq -r '.type' <<< "$PR")" \ @@ -135,12 +142,12 @@ jobs: ' done < <(jq -c '.blocking[]' <<< "${{steps.pr_ids.outputs.prs}}") | jq -s ) - echo "state=$BLOCKED_PR_DATA" >> "$GITHUB_OUPUT" + 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) }} + 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 }} @@ -154,7 +161,7 @@ jobs: - 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) }} + 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 }} @@ -167,7 +174,7 @@ jobs: - name: Apply 'blocking' Label to Dependencies if Missing id: label_blocking - if: ${{ fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 }} + if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 continue-on-error: true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -187,7 +194,7 @@ jobs: - name: Apply Blocking PR Status Check id: blocked_check - if: ${{ fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 }} + if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 continue-on-error: true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -210,7 +217,7 @@ jobs: - name: Context Comment id: blocked_comment - if: ${{ fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 }} + if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 continue-on-error: true run: | COMMENT_PATH="$(pwd)/temp_comment_file.txt" @@ -227,10 +234,11 @@ jobs: echo "file_path=${COMMENT_PATH}" >> "$GITHUB_OUTPUT" - name: 💬 PR Comment - if: ${{ fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 }} + 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" + diff --git a/.github/workflows/merge_blocking_pr.yml b/.github/workflows/merge_blocking_pr.yml new file mode 100644 index 000000000..fd52da295 --- /dev/null +++ b/.github/workflows/merge_blocking_pr.yml @@ -0,0 +1,80 @@ +name: Merged Blocking Pull Request Automation + +on: + pull_request: + types: + - closed + +jobs: + update_blocked_status: + name: Update Blocked Status + runs-on: ubuntu-latest + + if: github.event.pull_request.merged == true + + permissions: + issues: write + pull-requests: write + statuses: write + checks: write + + steps: + - name: Gather Dependent PRs + id: gather_deps + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + run: | + owner="${{ github.repository_owner }}" + owner_repo="${{ github.repository }}" + repo="${owner_repo#"${owner}/"}" + blocked_prs=$( + gh api graphql \ + -f repo="$repo" \ + -f owner="$owner" \ + -f query=' + query($repo: String!, $owner: String!, $endCursor: String) { + repository(name: $repo, owner: $owner) { + pullRequests(first: 100, after: $endCursor, states: [OPEN], labels: ["blocked"]) { + nodes { + number + bodyText + merged + } + pageInfo { + hasNextPage + endCursor + } + } + } + } + ' \ + --paginate \ + --slurp \ + | jq --argjson pr "${{ github.event.pull_request.number }}" ' + [.[].data.repository.pullRequests.nodes[]] | .[] | select( + .bodyText | + scan("(?:blocked (?:by|on)|stacked on):? #(?[0-9]+)") | + map(tonumber) | + any(.[]; . == $pr) + ) + ' + ) + echo "deps=$blocked_prs" >> "$GITHUB_OUTPUT" + echo "numdeps='$(jq -r '. | length' <<< "$blocked_prs")" + + - name: Trigger Blocked PP Workflows for Dependants + if: fromJSON(steps.gather_deps.outputs.numdeps) > 0 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + while read -r pr ; do + gh api \ + --method POST \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "/repos/${{ github.repository }}/actions/workflows/blocked_prs.yml/dispatches" \ + -f "ref=${{ github.ref_name }}" \ + -f "inputs[pr_id]=$pr" + done < <(jq -c '.[].number' <<< "${{steps.gather_deps.outputs.deps}}") + From cfc3c767791d5c2da170fd8ccd1199e1c8028d13 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 12 Mar 2025 13:01:20 -0700 Subject: [PATCH 03/27] ci: fix labels detected as sequence Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/blocked_prs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/blocked_prs.yml b/.github/workflows/blocked_prs.yml index ecafc65a8..43915776d 100644 --- a/.github/workflows/blocked_prs.yml +++ b/.github/workflows/blocked_prs.yml @@ -24,7 +24,7 @@ jobs: checks: write steps: - - name: Setup From Pull Request Vent + - name: Setup From Pull Request Event if: github.event_name != 'workflow_dispatch' id: pr_event_setup env: @@ -35,7 +35,7 @@ jobs: PR_NUMBER_L: ${{ github.event.pull_request.number }} PR_HEAD_LABEL_L: ${{ github.event.pull_request.head.label }} PR_BODY_L: ${{ github.event.pull_request.body }} - PR_LABLES_L: ${{ github.event.pull_request.labels }} + PR_LABLES_L: "${{ github.event.pull_request.labels }}" run: | # setup env for the rest of the workflow { From cbd1fc6f43eb4872cac250f1c074dc2c650f96a0 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 12 Mar 2025 13:26:02 -0700 Subject: [PATCH 04/27] ci: fix repo URL in blocked pr workflow Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/blocked_prs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/blocked_prs.yml b/.github/workflows/blocked_prs.yml index 43915776d..04ecd038e 100644 --- a/.github/workflows/blocked_prs.yml +++ b/.github/workflows/blocked_prs.yml @@ -30,7 +30,7 @@ jobs: env: REPO_L: ${{ github.event.pull_request.base.repo.name }} OWNER_L: ${{ github.event.pull_request.base.repo.owner.login }} - REPO_URL_L: $ {{ github.event.pull_request.base.repo.html_url }} + REPO_URL_L: ${{ github.event.pull_request.base.repo.html_url }} PR_HEAD_SHA_L: ${{ github.event.pull_request.head.sha }} PR_NUMBER_L: ${{ github.event.pull_request.number }} PR_HEAD_LABEL_L: ${{ github.event.pull_request.head.label }} @@ -56,7 +56,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} OWNER_REPO_L: ${{ github.repository }} OWNER_L: ${{ github.repository_owner }} - REPO_URL_L: $ {{ github.repositoryUrl }} + REPO_URL_L: https://github.com/${{ github.repository }} PR_NUMBER_L: ${{ inputs.pr_id }} run: | # setup env for the rest of the workflow From a2907dcaa356c22f6e58f82947bd1829e85abb7f Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 12 Mar 2025 14:05:46 -0700 Subject: [PATCH 05/27] ci: fix blocked PR workflow to preserve JSON in ENV Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/blocked_prs.yml | 124 ++++++++++++++++-------------- 1 file changed, 65 insertions(+), 59 deletions(-) diff --git a/.github/workflows/blocked_prs.yml b/.github/workflows/blocked_prs.yml index 04ecd038e..ed07036af 100644 --- a/.github/workflows/blocked_prs.yml +++ b/.github/workflows/blocked_prs.yml @@ -28,25 +28,25 @@ jobs: if: github.event_name != 'workflow_dispatch' id: pr_event_setup env: - REPO_L: ${{ github.event.pull_request.base.repo.name }} - OWNER_L: ${{ github.event.pull_request.base.repo.owner.login }} - REPO_URL_L: ${{ github.event.pull_request.base.repo.html_url }} - PR_HEAD_SHA_L: ${{ github.event.pull_request.head.sha }} - PR_NUMBER_L: ${{ github.event.pull_request.number }} - PR_HEAD_LABEL_L: ${{ github.event.pull_request.head.label }} - PR_BODY_L: ${{ github.event.pull_request.body }} - PR_LABLES_L: "${{ github.event.pull_request.labels }}" + PR_JSON: ${{ toJSON(github.event.pull_request) }} run: | # setup env for the rest of the workflow { - echo "REPO=$REPO_L" - echo "OWNER=$OWNER_L" - echo "REPO_URL=$REPO_URL_L" - echo "PR_NUMBER=$PR_NUMBER_L" - echo "PR_HEAD_SHA=$PR_HEAD_SHA_L" - echo "PR_HEAD_LABEL=$PR_HEAD_LABEL_L" - echo "PR_BODY=$PR_BODY_L" - echo "PR_LABELS=$(jq 'reduce .[].name as $l ([]; . + [$l])' <<< "$PR_LABELS_L" )" + 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" - name: Setup From Dispatch Event @@ -54,33 +54,35 @@ jobs: id: dispatch_event_setup env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OWNER_REPO_L: ${{ github.repository }} - OWNER_L: ${{ github.repository_owner }} - REPO_URL_L: https://github.com/${{ github.repository }} - PR_NUMBER_L: ${{ inputs.pr_id }} + 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_L/" - REPO_L="${OWNER_REPO_L#"$owner_prefix"}" - PR_L=$( + owner_prefix="$OWNER/" + REPO="${OWNER_REPO#"$owner_prefix"}" + PR_JSON=$( gh api \ - -H "Accept: application/vnd.github.text+json" \ + -H "Accept: application/vnd.github.raw+json" \ -H "X-GitHub-Api-Version: 2022-11-28" \ - "/repos/$OWNER_L/$REPO_L/pulls/$PR_NUMBER_L" + "/repos/$OWNER/$REPO/pulls/$PR_NUMBER" ) - PR_HEAD_SHA_L=$(jq -r '.head.sha' <<< "$PR_L") - PR_HEAD_LABEL_L=$(jq -r '.head.label' <<< "$PR_L") - PR_BODY_L=$(jq -r '.body_text' <<< "$PR_L") - PR_LABELS_L=$(jq '.labels' <<< "$PR_L") { - echo "REPO=$REPO_L" - echo "OWNER=$OWNER_L" - echo "REPO_URL=$REPO_URL_L" - echo "PR_NUMBER=$PR_NUMBER_L" - echo "PR_HEAD_SHA=$PR_HEAD_SHA_L" - echo "PR_HEAD_LABEL=$PR_HEAD_LABEL_L" - echo "PR_BODY=$PR_BODY_L" - echo "PR_LABELS=$(jq 'reduce .[].name as $l ([]; . + [$l])' <<< "$PR_LABELS_L" )" + 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" @@ -89,7 +91,7 @@ jobs: run: | PRS=$( jq ' - . as $body + .prBody as $body | ( $body | scan("blocked (?(?by)|(?on)):? #(?[0-9]+)") | map({ @@ -109,7 +111,7 @@ jobs: "blocking": $prs, "numBlocking": ( $prs | length), } - ' <<< "$PR_BODY" + ' <<< "$JOB_DATA" ) echo "prs=$PRS" >> "$GITHUB_OUTPUT" @@ -120,12 +122,12 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | BLOCKED_PR_DATA=$( - while read -r PR ; do + 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")" \ - | jq --arg type "$(jq -r '.type' <<< "$PR")" \ + "/repos/$OWNER/$REPO/pulls/$(jq -r '.number' <<< "$pr_data")" \ + | jq --arg type "$(jq -r '.type' <<< "$pr_data")" \ ' . | { "type": $type, @@ -180,14 +182,14 @@ jobs: 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") + 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" \ + "/repos/$OWNER/$REPO/issues/$pr/labels" \ -f "labels[]=blocking" fi done < <(jq -c '.[]' <<< "${{steps.blocked_data.outputs.state}}") @@ -199,20 +201,21 @@ jobs: 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 + while read -r pr_data ; do DESC=$( - jq -r ' "Blocking PR #" + (.number | tostring) + " is " + (if .merged then "" else "not yet " end) + "merged"' <<< "$PR_DATA" + 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" )" \ + "/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")" + -f "context=continuous-integration/blocked-pr-check:$(jq '.number' <<< "$pr_data")" done < <(jq -c '.[]' <<< "${{steps.blocked_data.outputs.state}}") - name: Context Comment @@ -223,13 +226,16 @@ jobs: COMMENT_PATH="$(pwd)/temp_comment_file.txt" touch "$COMMENT_PATH" echo "" > "$COMMENT_PATH" - while read -r PR_DATA ; do - BASE_PR=$(jq '.number' <<< "$PR_DATA") - BASE_REF_NAME=$(jq '.baseRefName' <<< "$PR_DATA") - COMPARE_URL="$REPO_URL/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" + 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" From a6a172a1365e94c791ef457e47c7e9e34d693263 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 12 Mar 2025 15:21:08 -0700 Subject: [PATCH 06/27] ci: ensure block pr scan always returns valid json Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/blocked_prs.yml | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/.github/workflows/blocked_prs.yml b/.github/workflows/blocked_prs.yml index ed07036af..d6239eedb 100644 --- a/.github/workflows/blocked_prs.yml +++ b/.github/workflows/blocked_prs.yml @@ -44,7 +44,7 @@ jobs: "prHeadSha": .head.sha, "prHeadLabel": .head.label, "prBody": .body, - "prLabels": reduce .labels[].name as $l ([]; . + [$l]) + "prLabels": (reduce .labels[].name as $l ([]; . + [$l])) } ' <<< "$PR_JSON")" } >> "$GITHUB_ENV" @@ -80,7 +80,7 @@ jobs: "prHeadSha": .head.sha, "prHeadLabel": .head.label, "prBody": .body, - "prLabels": reduce .labels[].name as $l ([]; . + [$l]) + "prLabels": (reduce .labels[].name as $l ([]; . + [$l])) } ' <<< "$PR_JSON")" } >> "$GITHUB_ENV" @@ -93,18 +93,24 @@ jobs: jq ' .prBody as $body | ( - $body | scan("blocked (?(?by)|(?on)):? #(?[0-9]+)") - | map({ - "type": "Blocked on", - "number": ( . | tonumber ) - }) + $body | + reduce ( + . | scan("blocked (?:by|on):? #([0-9]+)") + | map({ + "type": "Blocked on", + "number": ( . | tonumber ) + }) + ) as $i ([]; . + [$i]) ) as $bprs | ( - $body | scan("stacked on:? #(?[0-9]+)") - | map({ - "type": "Stacked on", - "number": ( . | tonumber ) - }) + $body | + reduce ( + . | scan("stacked on:? #([0-9]+)") + | map({ + "type": "Stacked on", + "number": ( . | tonumber ) + }) + ) as $i ([]; . + [$i]) ) as $sprs | ($bprs + $sprs) as $prs | { From 0bbf529afb09dfcb9b08facc1b39aa4fe46312d3 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 12 Mar 2025 15:36:34 -0700 Subject: [PATCH 07/27] ci(blocked_prs): quote json in env file Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/blocked_prs.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/blocked_prs.yml b/.github/workflows/blocked_prs.yml index d6239eedb..bd8be64f3 100644 --- a/.github/workflows/blocked_prs.yml +++ b/.github/workflows/blocked_prs.yml @@ -28,14 +28,14 @@ jobs: if: github.event_name != 'workflow_dispatch' id: pr_event_setup env: - PR_JSON: ${{ toJSON(github.event.pull_request) }} + 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 ' + echo "JOB_DATA=\"$(jq ' { "repo": .base.repo.name, "owner": .base.repo.owner.login, @@ -46,8 +46,9 @@ jobs: "prBody": .body, "prLabels": (reduce .labels[].name as $l ([]; . + [$l])) } - ' <<< "$PR_JSON")" + ' <<< "$PR_JSON")\"" } >> "$GITHUB_ENV" + cat $GITHUB_ENV - name: Setup From Dispatch Event if: github.event_name == 'workflow_dispatch' @@ -71,7 +72,7 @@ jobs: 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 ' + echo "JOB_DATA=\"$(jq ' { "repo": .base.repo.name, "owner": .base.repo.owner.login, @@ -82,8 +83,9 @@ jobs: "prBody": .body, "prLabels": (reduce .labels[].name as $l ([]; . + [$l])) } - ' <<< "$PR_JSON")" + ' <<< "$PR_JSON")\"" } >> "$GITHUB_ENV" + cat $GITHUB_ENV - name: Find Blocked/Stacked PRs in body From eb11cde0f4b2966e4f3b632038f39c91800d3493 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 12 Mar 2025 15:50:43 -0700 Subject: [PATCH 08/27] ci(blocked_prs): use compact jq output when outputting to ENV or step ouput Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/blocked_prs.yml | 16 ++++++++-------- .github/workflows/merge_blocking_pr.yml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/blocked_prs.yml b/.github/workflows/blocked_prs.yml index bd8be64f3..6afe6e485 100644 --- a/.github/workflows/blocked_prs.yml +++ b/.github/workflows/blocked_prs.yml @@ -35,7 +35,7 @@ jobs: 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 ' + echo "JOB_DATA=$(jq -c ' { "repo": .base.repo.name, "owner": .base.repo.owner.login, @@ -46,7 +46,7 @@ jobs: "prBody": .body, "prLabels": (reduce .labels[].name as $l ([]; . + [$l])) } - ' <<< "$PR_JSON")\"" + ' <<< "$PR_JSON")" } >> "$GITHUB_ENV" cat $GITHUB_ENV @@ -72,7 +72,7 @@ jobs: 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 ' + echo "JOB_DATA=$(jq -c ' { "repo": .base.repo.name, "owner": .base.repo.owner.login, @@ -83,7 +83,7 @@ jobs: "prBody": .body, "prLabels": (reduce .labels[].name as $l ([]; . + [$l])) } - ' <<< "$PR_JSON")\"" + ' <<< "$PR_JSON")" } >> "$GITHUB_ENV" cat $GITHUB_ENV @@ -92,7 +92,7 @@ jobs: id: pr_ids run: | PRS=$( - jq ' + jq -c ' .prBody as $body | ( $body | @@ -135,7 +135,7 @@ jobs: -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")" \ + | jq -c --arg type "$(jq -r '.type' <<< "$pr_data")" \ ' . | { "type": $type, @@ -150,7 +150,7 @@ jobs: "baseRefName": .head.ref, } ' - done < <(jq -c '.blocking[]' <<< "${{steps.pr_ids.outputs.prs}}") | jq -s + done < <(jq -c '.blocking[]' <<< "${{steps.pr_ids.outputs.prs}}") | jq -c -s ) echo "state=$BLOCKED_PR_DATA" >> "$GITHUB_OUTPUT" echo "all_merged=$(jq 'all(.[].merged; .)' <<< "$BLOCKED_PR_DATA")" @@ -241,7 +241,7 @@ jobs: 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") + status=$(jq -r '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}}") diff --git a/.github/workflows/merge_blocking_pr.yml b/.github/workflows/merge_blocking_pr.yml index fd52da295..3123c83b3 100644 --- a/.github/workflows/merge_blocking_pr.yml +++ b/.github/workflows/merge_blocking_pr.yml @@ -51,7 +51,7 @@ jobs: ' \ --paginate \ --slurp \ - | jq --argjson pr "${{ github.event.pull_request.number }}" ' + | jq -c --argjson pr "${{ github.event.pull_request.number }}" ' [.[].data.repository.pullRequests.nodes[]] | .[] | select( .bodyText | scan("(?:blocked (?:by|on)|stacked on):? #(?[0-9]+)") | From 14f042540c3b38c3463737a85676d9f6f69c6055 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:11:21 -0700 Subject: [PATCH 09/27] ci(blocking_pr): pass json expressions as env not direct. use internal compost comment action Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/actions/create-comment/action.yml | 152 ++++++++++++++++++++++ .github/workflows/blocked_prs.yml | 31 +++-- .github/workflows/merge_blocking_pr.yml | 3 +- 3 files changed, 172 insertions(+), 14 deletions(-) create mode 100644 .github/actions/create-comment/action.yml diff --git a/.github/actions/create-comment/action.yml b/.github/actions/create-comment/action.yml new file mode 100644 index 000000000..5ba4bc5c9 --- /dev/null +++ b/.github/actions/create-comment/action.yml @@ -0,0 +1,152 @@ +name: Create Issue Comment +description: Create or updste an issue comment +inputs: + comment: + description: Comment Text + required: true + comment_path: + description: "Path to txt file to be parsed" + required: false + comment_id: + description: "Unique identifier for deduplicating comments" + default: "Create Issue Action" + required: false + issue_number: + description: Local Pull Request/Issue number to work on + required: true + gh_token: + description: gh api access token to use + required: true + repository: + description: the OWNER/REPOSITORY to operate on + required: true + +runs: + using: "composite" + steps: + - name: Generate Comment Text + shell: bash + env: + COMMENT_ID: ${{ inputs.comment_id }} + COMMENT_TEXT: ${{ inputs.comment }} + COMMENT_FILE: ${{ inputs.comment_path }} + run: | + comment_body="${COMMENT_TEXT}" + if [ -f "$COMMENT_FILE" ] ; then + echo "Reading comment file from ${COMMENT_FILE}" + comment_body="${comment_body}$(cat "$COMMENT_FILE")" + fi + echo "COMMENT_BODY=$comment_body" >> "$GITHUB_ENV" + + - name: Get Existing Comment Id + shell: bash + env: + GH_TOKEN: ${{ inputs.gh_token }} + ISSUE_NUMBER: ${{ inputs.issue_number }} + REPOSITORY: ${{ inputs.repository }} + COMMENT_ID: ${{ inputs.comment_id }} + run: | + owner=$(echo "$REPOSITORY" | cut -d '/' -f 1) + repo=$(echo "$REPOSITORY" | cut -d '/' -f 2) + data=$( + gh api graphql \ + --paginate \ + -f owner="$owner" \ + -f repo="$repo" \ + -F issue="$ISSUE_NUMBER" \ + -f query=' + query($repo: String!, $owner: String!, $issue: Int!, $endCursor: String) { + repository(name: $repo, owner: $owner) { + issueOrPullRequest(number: $issue) { + ... on Issue { + id + number + comments(first: 100, after: $endCursor) { + nodes { + id + body + } + pageInfo { + hasNextPage + endCursor + } + } + } + ... on PullRequest { + id + number + comments(first: 100, after: $endCursor) { + nodes { + id + body + } + pageInfo { + hasNextPage + endCursor + } + } + } + } + } + } + ' \ + --jq '' | jq -c --arg comment_id "" ' + .[0].data.repository.issueOrPullRequest.id as $id | + [ .[].data.repository.issueOrPullRequest.comments.nodes[] ] as $data | + [ $data.[] | select(.body | startswith($comment_id)) ] as $id_comments | + if ($id_comments | length) > 0 then + { "issueId": $id, "commentId": $id_comments[0].id } + else + { "issueId": $id, "commentId": "" } + end + ' + ) + echo "ISSUE_NODE_ID=$(jq -r '.issueId' <<< "$data")" >> "$GITHUB_ENV" + echo "COMMENT_NODE_ID=$(jq -r '.commentId' <<< "$data")" >> "$GITHUB_ENV" + + - name: Edit Existing Comment + if: env.COMMENT_NODE_ID != '' + shell: bash + env: + GH_TOKEN: ${{ inputs.gh_token }} + run: | + gh api graphql \ + -f comment_id="$COMMENT_NODE_ID" \ + -f comment_body="$COMMENT_BODY" \ + -f query=' + mutation($comment_id: ID!, $comment_body: String!) { + updateIssueComment(input: { + id: $comment_id, + body: $comment_body, + }) { + issueComment { + lastEditedAt + } + } + } + ' + + - name: Create Comment + if: env.COMMENT_NODE_ID == '' + shell: bash + env: + GH_TOKEN: ${{ inputs.gh_token }} + run: | + gh api graphql \ + -f issue_id="$ISSUE_NODE_ID" \ + -f comment_body="$COMMENT_BODY" \ + -f query=' + mutation ($issue_id: ID!, $comment_body: String!) { + addComment(input: { subjectId: $issue_id, body: $comment_body }) { + commentEdge { + node { + id + } + } + } + } + ' + + + + diff --git a/.github/workflows/blocked_prs.yml b/.github/workflows/blocked_prs.yml index 6afe6e485..d12f139bc 100644 --- a/.github/workflows/blocked_prs.yml +++ b/.github/workflows/blocked_prs.yml @@ -55,13 +55,12 @@ jobs: id: dispatch_event_setup env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OWNER_REPO: ${{ github.repository }} - OWNER: ${{ github.repository_owner }} + REPOSITORY: ${{ github.repository }} PR_NUMBER: ${{ inputs.pr_id }} run: | # setup env for the rest of the workflow - owner_prefix="$OWNER/" - REPO="${OWNER_REPO#"$owner_prefix"}" + OWNER=$(echo "$REPOSITORY" | cut -d '/' -f 1) + REPO=$(echo "$REPOSITORY" | cut -d '/' -f 2) PR_JSON=$( gh api \ -H "Accept: application/vnd.github.raw+json" \ @@ -124,10 +123,11 @@ jobs: echo "prs=$PRS" >> "$GITHUB_OUTPUT" - name: Collect Blocked PR Data - id: blocked_data + id: blocking_data if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BLOCKING_PRS: ${{ steps.pr_ids.outputs.prs }} run: | BLOCKED_PR_DATA=$( while read -r pr_data ; do @@ -150,14 +150,14 @@ jobs: "baseRefName": .head.ref, } ' - done < <(jq -c '.blocking[]' <<< "${{steps.pr_ids.outputs.prs}}") | jq -c -s + done < <(jq -c '.blocking[]' <<< "$BLOCKING_PRS") | jq -c -s ) - echo "state=$BLOCKED_PR_DATA" >> "$GITHUB_OUTPUT" - echo "all_merged=$(jq 'all(.[].merged; .)' <<< "$BLOCKED_PR_DATA")" + echo "data=$BLOCKED_PR_DATA" >> "$GITHUB_OUTPUT" + echo "all_merged=$(jq -r '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) + if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 && !contains(fromJSON(env.PR_LABELS), 'blocked') && !fromJSON(steps.blocking_data.outputs.all_merged) continue-on-error: true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -171,7 +171,7 @@ jobs: - 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) + if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 && fromJSON(steps.blocking_data.outputs.all_merged) continue-on-error: true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -188,6 +188,7 @@ jobs: continue-on-error: true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BLOCKING_DATA: ${{ steps.blocking_data.outputs.state }} run: | # label pr dependencies with 'blocking' if not already while read -r pr_data ; do @@ -200,7 +201,7 @@ jobs: "/repos/$OWNER/$REPO/issues/$pr/labels" \ -f "labels[]=blocking" fi - done < <(jq -c '.[]' <<< "${{steps.blocked_data.outputs.state}}") + done < <(jq -c '.[]' <<< "$BLOCKING_DATA") - name: Apply Blocking PR Status Check id: blocked_check @@ -208,6 +209,7 @@ jobs: continue-on-error: true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BLOCKING_DATA: ${{ steps.blocking_data.outputs.state }} run: | pr_head_sha=$(jq -r '.prHeadSha' <<< "$JOB_DATA") # create commit Status, overwrites previous identical context @@ -224,7 +226,7 @@ jobs: -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}}") + done < <(jq -c '.[]' <<< "$BLOCKING_DATA") - name: Context Comment id: blocked_comment @@ -250,9 +252,12 @@ jobs: - name: 💬 PR Comment if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 continue-on-error: true - uses: spicyparrot/pr-comment-action@v1.0.0 + uses: ./.github/actions/create-comment with: comment: "### PR Dependencies :pushpin:" comment_path: ${{ steps.blocked_comment.outputs.file_path }} comment_id: "block_pr_dependencies" + issue_number: ${{ env.PR_NUMBER }} + repository: ${{ github.repository }} + gh_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/merge_blocking_pr.yml b/.github/workflows/merge_blocking_pr.yml index 3123c83b3..8707dd95b 100644 --- a/.github/workflows/merge_blocking_pr.yml +++ b/.github/workflows/merge_blocking_pr.yml @@ -67,6 +67,7 @@ jobs: if: fromJSON(steps.gather_deps.outputs.numdeps) > 0 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DEPS: ${{ steps.gather_deps.outputs.deps }} run: | while read -r pr ; do gh api \ @@ -76,5 +77,5 @@ jobs: "/repos/${{ github.repository }}/actions/workflows/blocked_prs.yml/dispatches" \ -f "ref=${{ github.ref_name }}" \ -f "inputs[pr_id]=$pr" - done < <(jq -c '.[].number' <<< "${{steps.gather_deps.outputs.deps}}") + done < <(jq -c '.[].number' <<< "$DEPS") From 1c6ab1f0549e2b2d81ca19f4c83e631bb80b3677 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:28:44 -0700 Subject: [PATCH 10/27] ci(blocked_prs): unnest array of blocking prs Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/blocked_prs.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/blocked_prs.yml b/.github/workflows/blocked_prs.yml index d12f139bc..440fba16c 100644 --- a/.github/workflows/blocked_prs.yml +++ b/.github/workflows/blocked_prs.yml @@ -84,13 +84,11 @@ jobs: } ' <<< "$PR_JSON")" } >> "$GITHUB_ENV" - cat $GITHUB_ENV - - name: Find Blocked/Stacked PRs in body id: pr_ids run: | - PRS=$( + prs=$( jq -c ' .prBody as $body | ( @@ -101,7 +99,7 @@ jobs: "type": "Blocked on", "number": ( . | tonumber ) }) - ) as $i ([]; . + [$i]) + ) as $i ([]; . + [$i[]]) ) as $bprs | ( $body | @@ -111,7 +109,7 @@ jobs: "type": "Stacked on", "number": ( . | tonumber ) }) - ) as $i ([]; . + [$i]) + ) as $i ([]; . + [$i[]]) ) as $sprs | ($bprs + $sprs) as $prs | { @@ -120,7 +118,7 @@ jobs: } ' <<< "$JOB_DATA" ) - echo "prs=$PRS" >> "$GITHUB_OUTPUT" + echo "prs=$prs" >> "$GITHUB_OUTPUT" - name: Collect Blocked PR Data id: blocking_data From 6fd70ad095e15b25b47a27575c6cabebfcd67a04 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:35:41 -0700 Subject: [PATCH 11/27] ci(blocked_prs): correct condition to use JOB_DATA from env Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/blocked_prs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/blocked_prs.yml b/.github/workflows/blocked_prs.yml index 440fba16c..b16df71d7 100644 --- a/.github/workflows/blocked_prs.yml +++ b/.github/workflows/blocked_prs.yml @@ -155,7 +155,7 @@ jobs: - 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.blocking_data.outputs.all_merged) + if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 && !contains(fromJSON(env.JOB_DATA).prLabels, 'blocked') && !fromJSON(steps.blocking_data.outputs.all_merged) continue-on-error: true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From e00d93135f23d3f9dadbdc950099226832cec4bf Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:47:43 -0700 Subject: [PATCH 12/27] ci(blocked_prs): fix blocking_data refrences, add synchronize and reopend events Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/blocked_prs.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/blocked_prs.yml b/.github/workflows/blocked_prs.yml index b16df71d7..b6169768c 100644 --- a/.github/workflows/blocked_prs.yml +++ b/.github/workflows/blocked_prs.yml @@ -4,7 +4,9 @@ on: pull_request_target: types: - opened + - reopened - edited + - synchronize workflow_dispatch: inputs: pr_id: @@ -151,7 +153,7 @@ jobs: done < <(jq -c '.blocking[]' <<< "$BLOCKING_PRS") | jq -c -s ) echo "data=$BLOCKED_PR_DATA" >> "$GITHUB_OUTPUT" - echo "all_merged=$(jq -r 'all(.[].merged; .)' <<< "$BLOCKED_PR_DATA")" + echo "all_merged=$(jq -r 'all(.[].merged; .)' <<< "$BLOCKED_PR_DATA")" >> "$GITHUB_OUTPUT" - name: Apply Blocked Label if Missing id: label_blocked @@ -186,7 +188,7 @@ jobs: continue-on-error: true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BLOCKING_DATA: ${{ steps.blocking_data.outputs.state }} + BLOCKING_DATA: ${{ steps.blocking_data.outputs.data }} run: | # label pr dependencies with 'blocking' if not already while read -r pr_data ; do @@ -207,7 +209,7 @@ jobs: continue-on-error: true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BLOCKING_DATA: ${{ steps.blocking_data.outputs.state }} + BLOCKING_DATA: ${{ steps.blocking_data.outputs.data }} run: | pr_head_sha=$(jq -r '.prHeadSha' <<< "$JOB_DATA") # create commit Status, overwrites previous identical context From 79283fd74446ba096004e66d900cf17913163318 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:57:24 -0700 Subject: [PATCH 13/27] ci(blocked_prs): checkout default branch for action access Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/blocked_prs.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/blocked_prs.yml b/.github/workflows/blocked_prs.yml index b6169768c..e89cd9dde 100644 --- a/.github/workflows/blocked_prs.yml +++ b/.github/workflows/blocked_prs.yml @@ -26,6 +26,10 @@ jobs: checks: write steps: + - name: Checkout Default Branch + uses: actions/checkout@v4 + with: + ref: ${{ github.event.repository.default_branch }} - name: Setup From Pull Request Event if: github.event_name != 'workflow_dispatch' id: pr_event_setup From 0474b03626e6aff15630e4965fe73a5136d21ea0 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 12 Mar 2025 20:01:29 -0700 Subject: [PATCH 14/27] ci(blocked_pr): fix comment action Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/actions/create-comment/action.yml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/actions/create-comment/action.yml b/.github/actions/create-comment/action.yml index 5ba4bc5c9..52b46d3b2 100644 --- a/.github/actions/create-comment/action.yml +++ b/.github/actions/create-comment/action.yml @@ -51,6 +51,7 @@ runs: data=$( gh api graphql \ --paginate \ + --slurp \ -f owner="$owner" \ -f repo="$repo" \ -F issue="$ISSUE_NUMBER" \ @@ -90,16 +91,16 @@ runs: } } ' \ - --jq '' | jq -c --arg comment_id "" ' - .[0].data.repository.issueOrPullRequest.id as $id | - [ .[].data.repository.issueOrPullRequest.comments.nodes[] ] as $data | - [ $data.[] | select(.body | startswith($comment_id)) ] as $id_comments | - if ($id_comments | length) > 0 then - { "issueId": $id, "commentId": $id_comments[0].id } - else - { "issueId": $id, "commentId": "" } - end - ' + | jq -c --arg comment_id "" ' + .[0].data.repository.issueOrPullRequest.id as $id | + [ .[].data.repository.issueOrPullRequest.comments.nodes[] ] as $data | + [ $data.[] | select(.body | startswith($comment_id)) ] as $id_comments | + if ($id_comments | length) > 0 then + { "issueId": $id, "commentId": $id_comments[0].id } + else + { "issueId": $id, "commentId": "" } + end + ' ) echo "ISSUE_NODE_ID=$(jq -r '.issueId' <<< "$data")" >> "$GITHUB_ENV" echo "COMMENT_NODE_ID=$(jq -r '.commentId' <<< "$data")" >> "$GITHUB_ENV" From 05356eff6939a9511d381f13e476713a188bbdac Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 12 Mar 2025 20:12:02 -0700 Subject: [PATCH 15/27] ci(blocking_prs): fix generated comment Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/blocked_prs.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/blocked_prs.yml b/.github/workflows/blocked_prs.yml index e89cd9dde..b83c01c0c 100644 --- a/.github/workflows/blocked_prs.yml +++ b/.github/workflows/blocked_prs.yml @@ -236,6 +236,8 @@ jobs: id: blocked_comment if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 continue-on-error: true + env: + BLOCKING_DATA: ${{ steps.blocking_data.outputs.data }} run: | COMMENT_PATH="$(pwd)/temp_comment_file.txt" touch "$COMMENT_PATH" @@ -250,7 +252,7 @@ jobs: status=$(jq -r '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}}") + done < <(jq -c '.[]' <<< "$BLOCKING_DATA") echo "file_path=${COMMENT_PATH}" >> "$GITHUB_OUTPUT" - name: 💬 PR Comment @@ -258,7 +260,7 @@ jobs: continue-on-error: true uses: ./.github/actions/create-comment with: - comment: "### PR Dependencies :pushpin:" + comment: "

PR Dependencies :pushpin:

" comment_path: ${{ steps.blocked_comment.outputs.file_path }} comment_id: "block_pr_dependencies" issue_number: ${{ env.PR_NUMBER }} From c2b7b4c82aebc88adf12fda75e2350a0e3fb6a05 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 12 Mar 2025 20:18:17 -0700 Subject: [PATCH 16/27] ci(blocked_prs): fix multiline comment body in comment action Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/actions/create-comment/action.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/actions/create-comment/action.yml b/.github/actions/create-comment/action.yml index 52b46d3b2..5e5925504 100644 --- a/.github/actions/create-comment/action.yml +++ b/.github/actions/create-comment/action.yml @@ -36,7 +36,9 @@ runs: echo "Reading comment file from ${COMMENT_FILE}" comment_body="${comment_body}$(cat "$COMMENT_FILE")" fi - echo "COMMENT_BODY=$comment_body" >> "$GITHUB_ENV" + echo 'COMMENT_BODY<> "$GITHUB_ENV" + echo "$comment_body" >> "$GITHUB_ENV" + echo 'EOF' >> "$GITHUB_ENV" - name: Get Existing Comment Id shell: bash From c75ae4170f3570c1577d088470a8eef8346e6638 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 12 Mar 2025 20:40:33 -0700 Subject: [PATCH 17/27] ci(blocked_prs): fix merge workflow Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/merge_blocking_pr.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/merge_blocking_pr.yml b/.github/workflows/merge_blocking_pr.yml index 8707dd95b..606ce7769 100644 --- a/.github/workflows/merge_blocking_pr.yml +++ b/.github/workflows/merge_blocking_pr.yml @@ -23,11 +23,9 @@ jobs: id: gather_deps env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - owner="${{ github.repository_owner }}" - owner_repo="${{ github.repository }}" - repo="${owner_repo#"${owner}/"}" + owner=$(echo "${{ github.repository }}" | cut -d '/' -f 1) + repo=$(echo "${{ github.repository }}" | cut -d '/' -f 2) blocked_prs=$( gh api graphql \ -f repo="$repo" \ @@ -52,16 +50,16 @@ jobs: --paginate \ --slurp \ | jq -c --argjson pr "${{ github.event.pull_request.number }}" ' - [.[].data.repository.pullRequests.nodes[]] | .[] | select( + reduce ( .[].data.repository.pullRequests.nodes[] | select( .bodyText | - scan("(?:blocked (?:by|on)|stacked on):? #(?[0-9]+)") | + scan("(?:blocked (?:by|on)|stacked on):? #([0-9]+)") | map(tonumber) | any(.[]; . == $pr) - ) + )) as $i ([]; . + [$i]) ' ) echo "deps=$blocked_prs" >> "$GITHUB_OUTPUT" - echo "numdeps='$(jq -r '. | length' <<< "$blocked_prs")" + echo "numdeps=$(jq -r '. | length' <<< "$blocked_prs")" >> "$GITHUB_OUTPUT" - name: Trigger Blocked PP Workflows for Dependants if: fromJSON(steps.gather_deps.outputs.numdeps) > 0 From 0818e7068aaca50b12c7e6a8ec8be30cd18de4ae Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 12 Mar 2025 21:01:58 -0700 Subject: [PATCH 18/27] ci(blocked_pres): ensure merge workflow has actions permission Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/merge_blocking_pr.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/merge_blocking_pr.yml b/.github/workflows/merge_blocking_pr.yml index 606ce7769..1127d2369 100644 --- a/.github/workflows/merge_blocking_pr.yml +++ b/.github/workflows/merge_blocking_pr.yml @@ -15,8 +15,7 @@ jobs: permissions: issues: write pull-requests: write - statuses: write - checks: write + actions: write steps: - name: Gather Dependent PRs From 7cbdb80f6ba17e1a85f61ecc408d61799b5c9273 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 14 Mar 2025 10:26:19 -0700 Subject: [PATCH 19/27] ci(label-actions): composit actions to add and delete labels in bulk Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/actions/add-labels/action.yml | 238 +++++++++++++++++++++ .github/actions/delete-labels/action.yml | 99 +++++++++ .github/workflows/manual-add-label.yml | 44 ++++ .github/workflows/manual-delete-labels.yml | 29 +++ 4 files changed, 410 insertions(+) create mode 100644 .github/actions/add-labels/action.yml create mode 100644 .github/actions/delete-labels/action.yml create mode 100644 .github/workflows/manual-add-label.yml create mode 100644 .github/workflows/manual-delete-labels.yml diff --git a/.github/actions/add-labels/action.yml b/.github/actions/add-labels/action.yml new file mode 100644 index 000000000..fdc8395b2 --- /dev/null +++ b/.github/actions/add-labels/action.yml @@ -0,0 +1,238 @@ +name: Add Label(s) +description: adds label(s) to labelable +inputs: + gh_token: + description: gh api access token to use + default: ${{ secrets.GITHUB_TOKEN }} + repository: + description: the OWNER/REPOSITORY to operate on + default: ${{ github.repository }} + issues: + description: a single or comma separated list of issue numbers + required: true + labels: + description: a single or comma separated list of labels to apply to all listed issues + required: true + colors: + description: | + A single or comma separated list of colors to create the labels with if needed. + the list order is the same as `labels`. Missing or blank values (e.g. `FFFFFF,,FFFFFF`) use the `default-color` + default-color: + description: default color to create labels with + default: "#D4C5F9" + +runs: + using: "composite" + steps: + - name: Collect Repo Labels + id: collect_labels + shell: bash + env: + GH_TOKEN: ${{ inputs.gh_token }} + REPOSITORY: ${{ inputs.repository }} + LABELS: ${{ inputs.labels }} + COLORS: ${{ inputs.colors }} + DEFAULT_COLOR: ${{ inputs.default-color }} + run: | + owner=$(echo "$REPOSITORY" | cut -d '/' -f 1) + repo=$(echo "$REPOSITORY" | cut -d '/' -f 2) + query=$( + jq -nr \ + --arg labels "$LABELS" \ + ' + (reduce ($labels | split (", *"; null) | .[]) as $i ([]; . + [$i])) as $labels + | "query ($owner: String!, $repo: String!) {\n" + + " repository(owner: $owner, name: $repo) {\n" + + " id\n" + + ( + reduce $labels[] as $i ([0, ""]; [ + .[0] + 1, + .[1] + ( + " _" + (.[0] | tostring) + + ": label(name: \"" + $i + "\") {\n" + + " id\n" + + " name\n"+ + " }\n" + ) + ]) + | .[1] + )+ + " }\n" + + "}" + ' + ) + data=$( + gh api graphql \ + -f owner="$owner" \ + -f repo="$repo" \ + -f query="$query" \ + | jq -c \ + --arg labels "$LABELS" \ + --arg colors "$COLORS" \ + --arg defaultColor "$DEFAULT_COLOR" \ + ' + . as $in + | ($labels | split(", *"; null)) as $labels + | ($colors | split(", *"; null)) as $colors + | ( + reduce ( [$labels, $colors] | transpose)[] as $i ( + {}; + .[$i[0]] = ( + if ($i[1] and $i[1] != "") then + $i[1] + else + $defaultColor + end + | if startswith("#") then .[1:] else . end + ) + ) + ) as $colorMap + | ( + reduce ( + $in.data.repository[] + | select( objects | .name as $name | any($labels[]; . == $name ) ) + ) as $i ({}; .[$i.name] = {"id": $i.id}) + ) as $found + | ( + reduce ( + $labels[] + | select( . as $name | $found | has($name) | not ) + ) as $i ({}; .[$i] = {"color": $colorMap[$i]}) + ) as $missing + | { + "repoId": $in.data.repository.id, + "found": $found, + "missing": $missing, + } + ' + ) + echo "found=$(jq -c '.found' <<< "$data")" >> "$GITHUB_OUTPUT" + echo "missing=$(jq -c '.missing' <<< "$data")" >> "$GITHUB_OUTPUT" + echo "repo_id=$(jq -r '.repoId' <<< "$data")" >> "$GITHUB_OUTPUT" + + - name: Collect Item Node IDs + id: collect_ids + shell: bash + env: + GH_TOKEN: ${{ inputs.gh_token }} + REPOSITORY: ${{ inputs.repository }} + ISSUES: ${{ inputs.labels }} + run: | + owner=$(echo "$REPOSITORY" | cut -d '/' -f 1) + repo=$(echo "$REPOSITORY" | cut -d '/' -f 2) + query=$( + jq -nr \ + --arg issues "$ISSUES" \ + ' + (reduce ($issues | split (", *"; null) | .[]) as $i ([]; . + [$i])) as $issues + | + "query ($owner: String!, $repo: String!) {\n" + + " repository(owner: $owner, name: $repo) {\n" + + ( + reduce $issues[] as $i ([0, ""]; [ + .[0] + 1, + .[1] + ( + " _" + (.[0] | tostring) + + ": issueOrPullRequest(number: " + ($i | tostring) +") {\n" + + " ... on Issue {\n" + + " id\n" + + " }\n" + + " ... on PullRequest {\n"+ + " id\n"+ + " }\n" + + " }\n" + ) + ]) + | .[1] + )+ + " }\n" + + "}" + ' + ) + data=$( + gh api graphql \ + -f owner="$owner" \ + -f repo="$repo" \ + -f query="$query" \ + | jq -c 'reduce .data.repository[].id as $i ([]; . + [$i])' + ) + echo "issue_ids=$data" >> "$GITHUB_OUTPUT" + + - name: Create Missing Labels + id: create_missing + shell: bash + env: + GH_TOKEN: ${{ inputs.gh_token }} + REPO_ID: ${{ steps.collect_labels.outputs.repo_id }} + LABELS: ${{ steps.collect_labels.outputs.labels }} + MISSING: ${{ steps.collect_labels.outputs.missing }} + run: | + query=$( + jq -nr \ + --argjson labels "$MISSING" \ + --arg repo "$REPO_ID" \ + ' + "mutation {\n" + ( + reduce ($labels | keys | .[] | [., $labels[.]]) as $i ([0, ""]; [ + .[0] + 1, + .[1] + ( + " _" + (.[0] | tostring) + + ": createLabel(input: {repositoryId: \"" + $repo + + "\", name: \"" + $i[0] + + "\", color: \"" + $i[1].color + + "\"}) {\n" + + " clientMutationId\n" + + " label {\n" + + " id\n" + + " name\n" + + " color\n" + + " }\n" + + " }\n" + ) + ]) + | .[1] + ) + + "}" + ' + ) + data=$( + gh api graphql -f query="$query" \ + | jq --argjson existing "$LABELS" \ + ' + reduce .data[].label as $i ({}; .[$i.name] = {"id": $i.id, "color": $i.color }) + | . + $existing + ' + ) + lable_ids=$(jq -c '[.[].id]' <<< "$data") + echo "label_ids=$lable_ids" >> "$GITHUB_OUTPUT" + + - name: Apply Labels + id: apply_labels + shell: bash + env: + GH_TOKEN: ${{ inputs.gh_token }} + ISSUES: ${{ steps.collect_ids.outputs.issue_ids }} + LABELS: ${{ steps.create_missing.outputs.label_ids }} + run: | + query=$( + jq -nr \ + --argjson labels "$LABELS" \ + --argjson issues "$ISSUES" \ + ' + "mutation {\n" + ( + reduce $issues[] as $i ([0, ""]; [ + .[0] + 1, + .[1] + ( + " _" + (.[0] | tostring) + + ": addLabelsToLabelable(input: {labelableId: \"" + + $i + "\", labelIds: " + ($labels | tojson) + "}) {\n" + + " clientMutationId\n" + + " }\n" + ) + ]) + | .[1] + ) + + "}" + ' + ) + gh api graphql -f query="$query" diff --git a/.github/actions/delete-labels/action.yml b/.github/actions/delete-labels/action.yml new file mode 100644 index 000000000..9ca921283 --- /dev/null +++ b/.github/actions/delete-labels/action.yml @@ -0,0 +1,99 @@ +name: Delete Label(s) +description: delete Label(s) +inputs: + gh_token: + description: gh api access token to use + default: ${{ secrets.GITHUB_TOKEN }} + repository: + description: the OWNER/REPOSITORY to operate on + default: ${{ github.repository }} + labels: + description: a single or comma separated list of labels to delete + required: true + +runs: + using: "composite" + steps: + - name: Collect Repo Labels + id: collect_labels + shell: bash + env: + GH_TOKEN: ${{ inputs.gh_token }} + REPOSITORY: ${{ inputs.repository }} + LABELS: ${{ inputs.labels }} + run: | + owner=$(echo "$REPOSITORY" | cut -d '/' -f 1) + repo=$(echo "$REPOSITORY" | cut -d '/' -f 2) + query=$( + jq -nr \ + --arg labels "$LABELS" \ + ' + (reduce ($labels | split (", *"; null) | .[]) as $i ([]; . + [$i])) as $labels + | "query ($owner: String!, $repo: String!) {\n" + + " repository(owner: $owner, name: $repo) {\n" + + ( + reduce $labels[] as $i ([0, ""]; [ + .[0] + 1, + .[1] + ( + " _" + (.[0] | tostring) + + ": label(name: \"" + $i + "\") {\n" + + " id\n" + + " name\n"+ + " }\n" + ) + ]) + | .[1] + )+ + " }\n" + + "}" + ' + ) + data=$( + gh api graphql \ + -f owner="$owner" \ + -f repo="$repo" \ + -f query="$query" \ + | jq -c \ + --arg labels "$LABELS" \ + --arg colors "$COLORS" \ + --arg defaultColor "$DEFAULT_COLOR" \ + ' + . as $in + | ($labels | split(", *"; null)) as $labels + | ( + reduce ( + $in.data.repository[] + | select( objects | .name as $name | any($labels[]; . == $name ) ) + ) as $i ({}; .[$i.name] = {"id": $i.id}) + ) as $found + | [.[].id] + ' + ) + echo "label_ids=$data" >>> "$GITHUB_OUTPUT" + + - name: Delete Labels + id: delete_labels + shell: bash + env: + GH_TOKEN: ${{ inputs.gh_token }} + LABELS: ${{ steps.collect_labels.outputs.label_ids }} + run: | + query=$(jq -r ' + . as $in + | ( + "mutation {\n" + ( + reduce $in[] as $id ([0, ""]; [ + .[0] + 1 , + .[1] + ( + " _" + (.[0] | tostring) + ": deleteLabel(input: {id: \"" + $id + "\"}) {\n" + + " clientMutationId\n" + + " }\n" + ) + ]) + | .[1] + ) + + "}" + ) + ' <<< "$LABELS" + ) + gh api graphql -f query="$query" diff --git a/.github/workflows/manual-add-label.yml b/.github/workflows/manual-add-label.yml new file mode 100644 index 000000000..e6c768a7d --- /dev/null +++ b/.github/workflows/manual-add-label.yml @@ -0,0 +1,44 @@ +name: Manual workflow to apply labels in bulk + +on: + workflow_dispatch: + inputs: + issues: + description: a single or comma separated list of issue numbers + required: true + type: string + labels: + description: a single or comma separated list of labels to apply to all listed issues + required: true + type: string + colors: + description: | + A single or comma separated list of colors to create the labels with if needed. + the list order is the same as `labels`. Missing or blank values (e.g. `FFFFFF,,FFFFFF`) use the `default_color` + type: string + default-color: + description: default color to create labels with + default: "#D4C5F9" + type: string + +jobs: + apply-labels: + name: Apply Labels + runs-on: ubuntu-latest + + permissions: + issues: write + pull-requests: write + + steps: + - name: Checkout Default Branch + uses: actions/checkout@v4 + with: + ref: ${{ github.event.repository.default_branch }} + - name: Run Label Action + uses: ./.github/actions/add-labels + with: + issues: ${{ inputs.issues }} + labels: ${{ inputs.labels }} + colors: ${{ inputs.colors }} + default-color: ${{ inputs.default-color }} diff --git a/.github/workflows/manual-delete-labels.yml b/.github/workflows/manual-delete-labels.yml new file mode 100644 index 000000000..b575d59d8 --- /dev/null +++ b/.github/workflows/manual-delete-labels.yml @@ -0,0 +1,29 @@ + +name: Manual workflow to delete labels in bulk + +on: + workflow_dispatch: + inputs: + labels: + description: a single or comma separated list of labels to delete + required: true + type: string + +jobs: + delete-labels: + name: Delete Labels + runs-on: ubuntu-latest + + permissions: + issues: write + pull-requests: write + + steps: + - name: Checkout Default Branch + uses: actions/checkout@v4 + with: + ref: ${{ github.event.repository.default_branch }} + - name: Run Label Action + uses: ./.github/actions/delete-labels + with: + labels: ${{ inputs.labels }} From da3f378d5d52b56ef5360d72e2fec7f466ec6190 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 14 Mar 2025 10:35:35 -0700 Subject: [PATCH 20/27] ci(label-actions): actions can't directly access secrets Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/actions/add-labels/action.yml | 23 ++++++++++------------ .github/actions/delete-labels/action.yml | 6 +++--- .github/workflows/manual-add-label.yml | 3 ++- .github/workflows/manual-delete-labels.yml | 3 ++- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/.github/actions/add-labels/action.yml b/.github/actions/add-labels/action.yml index fdc8395b2..9adb87945 100644 --- a/.github/actions/add-labels/action.yml +++ b/.github/actions/add-labels/action.yml @@ -3,7 +3,7 @@ description: adds label(s) to labelable inputs: gh_token: description: gh api access token to use - default: ${{ secrets.GITHUB_TOKEN }} + required: true repository: description: the OWNER/REPOSITORY to operate on default: ${{ github.repository }} @@ -116,7 +116,7 @@ runs: env: GH_TOKEN: ${{ inputs.gh_token }} REPOSITORY: ${{ inputs.repository }} - ISSUES: ${{ inputs.labels }} + ISSUES: ${{ inputs.issues }} run: | owner=$(echo "$REPOSITORY" | cut -d '/' -f 1) repo=$(echo "$REPOSITORY" | cut -d '/' -f 2) @@ -164,14 +164,13 @@ runs: env: GH_TOKEN: ${{ inputs.gh_token }} REPO_ID: ${{ steps.collect_labels.outputs.repo_id }} - LABELS: ${{ steps.collect_labels.outputs.labels }} + EXISTING: ${{ steps.collect_labels.outputs.found }} MISSING: ${{ steps.collect_labels.outputs.missing }} run: | query=$( jq -nr \ --argjson labels "$MISSING" \ - --arg repo "$REPO_ID" \ - ' + --arg repo "$REPO_ID" ' "mutation {\n" + ( reduce ($labels | keys | .[] | [., $labels[.]]) as $i ([0, ""]; [ .[0] + 1, @@ -196,15 +195,13 @@ runs: ' ) data=$( - gh api graphql -f query="$query" \ - | jq --argjson existing "$LABELS" \ - ' - reduce .data[].label as $i ({}; .[$i.name] = {"id": $i.id, "color": $i.color }) - | . + $existing - ' + gh api graphql -f query="$query" | jq --argjson existing "$EXISTING" ' + reduce .data[].label as $i ({}; .[$i.name] = {"id": $i.id, "color": $i.color }) + | . + $existing + ' ) - lable_ids=$(jq -c '[.[].id]' <<< "$data") - echo "label_ids=$lable_ids" >> "$GITHUB_OUTPUT" + label_ids=$(jq -c '[.[].id]' <<< "$data") + echo "label_ids=$label_ids" >> "$GITHUB_OUTPUT" - name: Apply Labels id: apply_labels diff --git a/.github/actions/delete-labels/action.yml b/.github/actions/delete-labels/action.yml index 9ca921283..25a8d5fad 100644 --- a/.github/actions/delete-labels/action.yml +++ b/.github/actions/delete-labels/action.yml @@ -3,7 +3,7 @@ description: delete Label(s) inputs: gh_token: description: gh api access token to use - default: ${{ secrets.GITHUB_TOKEN }} + required: true repository: description: the OWNER/REPOSITORY to operate on default: ${{ github.repository }} @@ -66,10 +66,10 @@ runs: | select( objects | .name as $name | any($labels[]; . == $name ) ) ) as $i ({}; .[$i.name] = {"id": $i.id}) ) as $found - | [.[].id] + | [$found[].id] ' ) - echo "label_ids=$data" >>> "$GITHUB_OUTPUT" + echo "label_ids=$data" >> "$GITHUB_OUTPUT" - name: Delete Labels id: delete_labels diff --git a/.github/workflows/manual-add-label.yml b/.github/workflows/manual-add-label.yml index e6c768a7d..8a06ae213 100644 --- a/.github/workflows/manual-add-label.yml +++ b/.github/workflows/manual-add-label.yml @@ -1,4 +1,4 @@ -name: Manual workflow to apply labels in bulk +name: Apply labels in bulk on: workflow_dispatch: @@ -38,6 +38,7 @@ jobs: - name: Run Label Action uses: ./.github/actions/add-labels with: + gh_token: ${{ secrets.GITHUB_TOKEN }} issues: ${{ inputs.issues }} labels: ${{ inputs.labels }} colors: ${{ inputs.colors }} diff --git a/.github/workflows/manual-delete-labels.yml b/.github/workflows/manual-delete-labels.yml index b575d59d8..e8c4b985d 100644 --- a/.github/workflows/manual-delete-labels.yml +++ b/.github/workflows/manual-delete-labels.yml @@ -1,5 +1,5 @@ -name: Manual workflow to delete labels in bulk +name: Delete labels in bulk on: workflow_dispatch: @@ -26,4 +26,5 @@ jobs: - name: Run Label Action uses: ./.github/actions/delete-labels with: + gh_token: ${{ secrets.GITHUB_TOKEN }} labels: ${{ inputs.labels }} From b42b453f76f5381d39de3420f7e18066b8930640 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 14 Mar 2025 12:58:42 -0700 Subject: [PATCH 21/27] ci(blocking-lables): cleanup lables with a pr closes Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/actions/add-labels/action.yml | 2 +- .github/actions/create-comment/action.yml | 4 +- .github/actions/delete-labels/action.yml | 2 + .../{blocked_prs.yml => blocked-prs.yml} | 68 ++++++------------- .github/workflows/manual-add-label.yml | 2 +- .github/workflows/manual-delete-labels.yml | 2 +- ..._blocking_pr.yml => merge-blocking_pr.yml} | 66 ++++++++++++------ 7 files changed, 74 insertions(+), 72 deletions(-) rename .github/workflows/{blocked_prs.yml => blocked-prs.yml} (80%) rename .github/workflows/{merge_blocking_pr.yml => merge-blocking_pr.yml} (52%) diff --git a/.github/actions/add-labels/action.yml b/.github/actions/add-labels/action.yml index 9adb87945..82b237497 100644 --- a/.github/actions/add-labels/action.yml +++ b/.github/actions/add-labels/action.yml @@ -19,7 +19,7 @@ inputs: the list order is the same as `labels`. Missing or blank values (e.g. `FFFFFF,,FFFFFF`) use the `default-color` default-color: description: default color to create labels with - default: "#D4C5F9" + default: "#ffffff" runs: using: "composite" diff --git a/.github/actions/create-comment/action.yml b/.github/actions/create-comment/action.yml index 5e5925504..484f1e5af 100644 --- a/.github/actions/create-comment/action.yml +++ b/.github/actions/create-comment/action.yml @@ -1,5 +1,5 @@ name: Create Issue Comment -description: Create or updste an issue comment +description: Create or update an issue comment inputs: comment: description: Comment Text @@ -19,7 +19,7 @@ inputs: required: true repository: description: the OWNER/REPOSITORY to operate on - required: true + default: ${{ github.repository }} runs: using: "composite" diff --git a/.github/actions/delete-labels/action.yml b/.github/actions/delete-labels/action.yml index 25a8d5fad..65d3ba3d4 100644 --- a/.github/actions/delete-labels/action.yml +++ b/.github/actions/delete-labels/action.yml @@ -70,10 +70,12 @@ runs: ' ) echo "label_ids=$data" >> "$GITHUB_OUTPUT" + echo "num_labels=$(jq -r 'length' <<< "$data")" >> "$GITHUB_OUTPUT" - name: Delete Labels id: delete_labels shell: bash + if: fromJSON( steps.collect_labels.outputs.num_labels ) > 0 env: GH_TOKEN: ${{ inputs.gh_token }} LABELS: ${{ steps.collect_labels.outputs.label_ids }} diff --git a/.github/workflows/blocked_prs.yml b/.github/workflows/blocked-prs.yml similarity index 80% rename from .github/workflows/blocked_prs.yml rename to .github/workflows/blocked-prs.yml index b83c01c0c..3201e5f7a 100644 --- a/.github/workflows/blocked_prs.yml +++ b/.github/workflows/blocked-prs.yml @@ -54,7 +54,6 @@ jobs: } ' <<< "$PR_JSON")" } >> "$GITHUB_ENV" - cat $GITHUB_ENV - name: Setup From Dispatch Event if: github.event_name == 'workflow_dispatch' @@ -133,7 +132,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BLOCKING_PRS: ${{ steps.pr_ids.outputs.prs }} run: | - BLOCKED_PR_DATA=$( + blocked_pr_data=$( while read -r pr_data ; do gh api \ -H "Accept: application/vnd.github+json" \ @@ -156,56 +155,33 @@ jobs: ' done < <(jq -c '.blocking[]' <<< "$BLOCKING_PRS") | jq -c -s ) - echo "data=$BLOCKED_PR_DATA" >> "$GITHUB_OUTPUT" - echo "all_merged=$(jq -r 'all(.[].merged; .)' <<< "$BLOCKED_PR_DATA")" >> "$GITHUB_OUTPUT" + blocked_by_labels=$(jq -c 'map( select( .merged | not ) | "blocked-by:" + (.number | tostring))' <<< "$blocked_pr_data" ) + echo "data=$blocked_pr_data" >> "$GITHUB_OUTPUT" + echo "all_merged=$(jq -r 'all(.[].merged; .)' <<< "$blocked_pr_data")" >> "$GITHUB_OUTPUT" + echo "blocked_by_labels=$blocked_by_labels" >> "$GITHUB_OUTPUT" + echo "current_blocking=$(jq -c 'map( select( .merged | not ) | .number )' <<< "$blocked_pr_data" )" >> "$GITHUB_OUTPUT" - - name: Apply Blocked Label if Missing + - name: Apply Blocked by Labels id: label_blocked - if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 && !contains(fromJSON(env.JOB_DATA).prLabels, 'blocked') && !fromJSON(steps.blocking_data.outputs.all_merged) + if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 && steps.blocking_data.outputs.blocked_by_labels != '' 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" + uses: ./.github/actions/add-labels + with: + repository: ${{ github.repository }} + gh_token: ${{ secrets.GITHUB_TOKEN }} + issues: ${{ env.PR_NUMBER }} + labels: ${{ join( fromJSON(steps.blocking_data.outputs.blocked_by_labels), ',' ) }} - - name: Remove 'blocked' Label if All Dependencies Are Merged - id: unlabel_blocked - if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 && fromJSON(steps.blocking_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 + - name: Apply 'blocking:' Label to Unmerged Dependencies id: label_blocking if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 continue-on-error: true - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BLOCKING_DATA: ${{ steps.blocking_data.outputs.data }} - 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 '.[]' <<< "$BLOCKING_DATA") + uses: ./.github/actions/add-labels + with: + repository: ${{ github.repository }} + gh_token: ${{ secrets.GITHUB_TOKEN }} + issues: ${{ join( fromJSON(steps.blocking_data.outputs.current_blocking) , ',' ) }} + labels: ${{ format( 'blocking:{0}', env.PR_NUMBER ) }} - name: Apply Blocking PR Status Check id: blocked_check @@ -229,7 +205,7 @@ jobs: -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")" + -f "context=ci/blocking-pr-check:$(jq '.number' <<< "$pr_data")" done < <(jq -c '.[]' <<< "$BLOCKING_DATA") - name: Context Comment diff --git a/.github/workflows/manual-add-label.yml b/.github/workflows/manual-add-label.yml index 8a06ae213..fdb69e389 100644 --- a/.github/workflows/manual-add-label.yml +++ b/.github/workflows/manual-add-label.yml @@ -1,4 +1,4 @@ -name: Apply labels in bulk +name: "Manual: Apply Labels in Bulk" on: workflow_dispatch: diff --git a/.github/workflows/manual-delete-labels.yml b/.github/workflows/manual-delete-labels.yml index e8c4b985d..300c0b52a 100644 --- a/.github/workflows/manual-delete-labels.yml +++ b/.github/workflows/manual-delete-labels.yml @@ -1,5 +1,5 @@ -name: Delete labels in bulk +name: "Manual: Delete labels in bulk" on: workflow_dispatch: diff --git a/.github/workflows/merge_blocking_pr.yml b/.github/workflows/merge-blocking_pr.yml similarity index 52% rename from .github/workflows/merge_blocking_pr.yml rename to .github/workflows/merge-blocking_pr.yml index 1127d2369..a3f40a1a8 100644 --- a/.github/workflows/merge_blocking_pr.yml +++ b/.github/workflows/merge-blocking_pr.yml @@ -6,11 +6,13 @@ on: - closed jobs: - update_blocked_status: + update-blocked-status: name: Update Blocked Status runs-on: ubuntu-latest - if: github.event.pull_request.merged == true + # a pr that was a `blocking:` label was merged. + # find the open pr's it was blocked by and trigger a refresh of their state + if: github.event.pull_request.merged == true && contains( join( github.event.pull_request.labels.*.name, ',' ), "blocking:" ) permissions: issues: write @@ -22,30 +24,32 @@ jobs: id: gather_deps env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} run: | owner=$(echo "${{ github.repository }}" | cut -d '/' -f 1) repo=$(echo "${{ github.repository }}" | cut -d '/' -f 2) + query=" + query(\$repo: String!, \$owner: String!, \$endCursor: String) { + repository(name: \$repo, owner: \$owner) { + pullRequests(first: 100, after: \$endCursor, states: [OPEN], labels: [\"blocked-by:${PR_NUMBER}\"]) { + nodes { + number + bodyText + merged + } + pageInfo { + hasNextPage + endCursor + } + } + } + } + " blocked_prs=$( gh api graphql \ -f repo="$repo" \ -f owner="$owner" \ - -f query=' - query($repo: String!, $owner: String!, $endCursor: String) { - repository(name: $repo, owner: $owner) { - pullRequests(first: 100, after: $endCursor, states: [OPEN], labels: ["blocked"]) { - nodes { - number - bodyText - merged - } - pageInfo { - hasNextPage - endCursor - } - } - } - } - ' \ + -f query="$query" \ --paginate \ --slurp \ | jq -c --argjson pr "${{ github.event.pull_request.number }}" ' @@ -60,7 +64,7 @@ jobs: echo "deps=$blocked_prs" >> "$GITHUB_OUTPUT" echo "numdeps=$(jq -r '. | length' <<< "$blocked_prs")" >> "$GITHUB_OUTPUT" - - name: Trigger Blocked PP Workflows for Dependants + - name: Trigger Blocked PR Workflows for Dependants if: fromJSON(steps.gather_deps.outputs.numdeps) > 0 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -71,8 +75,28 @@ jobs: --method POST \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2022-11-28" \ - "/repos/${{ github.repository }}/actions/workflows/blocked_prs.yml/dispatches" \ + "/repos/${{ github.repository }}/actions/workflows/blocked-prs.yml/dispatches" \ -f "ref=${{ github.ref_name }}" \ -f "inputs[pr_id]=$pr" done < <(jq -c '.[].number' <<< "$DEPS") + label-cleanup: + # this pr is closed, no need for these anymore + name: "Cleanup Related blocking: or blocked-by: labels" + runs-on: ubuntu-latest + + permissions: + issues: write + pull-requests: write + + steps: + - name: Checkout Default Branch + uses: actions/checkout@v4 + with: + ref: ${{ github.event.repository.default_branch }} + - name: Delete Related Labels + uses: ./.github/actions/delete-labels + with: + repository: ${{ github.repository }} + gh_token: ${{ secrets.GITHUB_TOKEN }} + labels: ${{ format('blocking:{0},blocked-by:{0}', github.event.pull_request.number ) }} From e8e81a762717b02af463baee61bda04a7d80c0d2 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 14 Mar 2025 14:56:46 -0700 Subject: [PATCH 22/27] use `basename` and `dirname` to split `owner/repo` Co-authored-by: Seth Flynn Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/actions/add-labels/action.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/actions/add-labels/action.yml b/.github/actions/add-labels/action.yml index 82b237497..0f36aff91 100644 --- a/.github/actions/add-labels/action.yml +++ b/.github/actions/add-labels/action.yml @@ -34,8 +34,8 @@ runs: COLORS: ${{ inputs.colors }} DEFAULT_COLOR: ${{ inputs.default-color }} run: | - owner=$(echo "$REPOSITORY" | cut -d '/' -f 1) - repo=$(echo "$REPOSITORY" | cut -d '/' -f 2) + owner="$(dirname "$REPOSITORY")" + repo="$(basename "$REPOSITORY")" query=$( jq -nr \ --arg labels "$LABELS" \ @@ -118,8 +118,8 @@ runs: REPOSITORY: ${{ inputs.repository }} ISSUES: ${{ inputs.issues }} run: | - owner=$(echo "$REPOSITORY" | cut -d '/' -f 1) - repo=$(echo "$REPOSITORY" | cut -d '/' -f 2) + owner="$(dirname "$REPOSITORY")" + repo="$(basename "$REPOSITORY")" query=$( jq -nr \ --arg issues "$ISSUES" \ From 72aee5c9f63a7ba710dcd46111f7955b10470029 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 14 Mar 2025 16:02:18 -0700 Subject: [PATCH 23/27] ci(blocked-pr): go back to single labels use gh cli actions directly insead of api where possible Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/actions/add-labels/action.yml | 235 --------------------- .github/actions/delete-labels/action.yml | 101 --------- .github/workflows/blocked-prs.yml | 44 ++-- .github/workflows/manual-add-label.yml | 45 ---- .github/workflows/manual-delete-labels.yml | 30 --- .github/workflows/merge-blocking-pr.yml | 52 +++++ .github/workflows/merge-blocking_pr.yml | 102 --------- 7 files changed, 76 insertions(+), 533 deletions(-) delete mode 100644 .github/actions/add-labels/action.yml delete mode 100644 .github/actions/delete-labels/action.yml delete mode 100644 .github/workflows/manual-add-label.yml delete mode 100644 .github/workflows/manual-delete-labels.yml create mode 100644 .github/workflows/merge-blocking-pr.yml delete mode 100644 .github/workflows/merge-blocking_pr.yml diff --git a/.github/actions/add-labels/action.yml b/.github/actions/add-labels/action.yml deleted file mode 100644 index 0f36aff91..000000000 --- a/.github/actions/add-labels/action.yml +++ /dev/null @@ -1,235 +0,0 @@ -name: Add Label(s) -description: adds label(s) to labelable -inputs: - gh_token: - description: gh api access token to use - required: true - repository: - description: the OWNER/REPOSITORY to operate on - default: ${{ github.repository }} - issues: - description: a single or comma separated list of issue numbers - required: true - labels: - description: a single or comma separated list of labels to apply to all listed issues - required: true - colors: - description: | - A single or comma separated list of colors to create the labels with if needed. - the list order is the same as `labels`. Missing or blank values (e.g. `FFFFFF,,FFFFFF`) use the `default-color` - default-color: - description: default color to create labels with - default: "#ffffff" - -runs: - using: "composite" - steps: - - name: Collect Repo Labels - id: collect_labels - shell: bash - env: - GH_TOKEN: ${{ inputs.gh_token }} - REPOSITORY: ${{ inputs.repository }} - LABELS: ${{ inputs.labels }} - COLORS: ${{ inputs.colors }} - DEFAULT_COLOR: ${{ inputs.default-color }} - run: | - owner="$(dirname "$REPOSITORY")" - repo="$(basename "$REPOSITORY")" - query=$( - jq -nr \ - --arg labels "$LABELS" \ - ' - (reduce ($labels | split (", *"; null) | .[]) as $i ([]; . + [$i])) as $labels - | "query ($owner: String!, $repo: String!) {\n" + - " repository(owner: $owner, name: $repo) {\n" + - " id\n" + - ( - reduce $labels[] as $i ([0, ""]; [ - .[0] + 1, - .[1] + ( - " _" + (.[0] | tostring) + - ": label(name: \"" + $i + "\") {\n" + - " id\n" + - " name\n"+ - " }\n" - ) - ]) - | .[1] - )+ - " }\n" + - "}" - ' - ) - data=$( - gh api graphql \ - -f owner="$owner" \ - -f repo="$repo" \ - -f query="$query" \ - | jq -c \ - --arg labels "$LABELS" \ - --arg colors "$COLORS" \ - --arg defaultColor "$DEFAULT_COLOR" \ - ' - . as $in - | ($labels | split(", *"; null)) as $labels - | ($colors | split(", *"; null)) as $colors - | ( - reduce ( [$labels, $colors] | transpose)[] as $i ( - {}; - .[$i[0]] = ( - if ($i[1] and $i[1] != "") then - $i[1] - else - $defaultColor - end - | if startswith("#") then .[1:] else . end - ) - ) - ) as $colorMap - | ( - reduce ( - $in.data.repository[] - | select( objects | .name as $name | any($labels[]; . == $name ) ) - ) as $i ({}; .[$i.name] = {"id": $i.id}) - ) as $found - | ( - reduce ( - $labels[] - | select( . as $name | $found | has($name) | not ) - ) as $i ({}; .[$i] = {"color": $colorMap[$i]}) - ) as $missing - | { - "repoId": $in.data.repository.id, - "found": $found, - "missing": $missing, - } - ' - ) - echo "found=$(jq -c '.found' <<< "$data")" >> "$GITHUB_OUTPUT" - echo "missing=$(jq -c '.missing' <<< "$data")" >> "$GITHUB_OUTPUT" - echo "repo_id=$(jq -r '.repoId' <<< "$data")" >> "$GITHUB_OUTPUT" - - - name: Collect Item Node IDs - id: collect_ids - shell: bash - env: - GH_TOKEN: ${{ inputs.gh_token }} - REPOSITORY: ${{ inputs.repository }} - ISSUES: ${{ inputs.issues }} - run: | - owner="$(dirname "$REPOSITORY")" - repo="$(basename "$REPOSITORY")" - query=$( - jq -nr \ - --arg issues "$ISSUES" \ - ' - (reduce ($issues | split (", *"; null) | .[]) as $i ([]; . + [$i])) as $issues - | - "query ($owner: String!, $repo: String!) {\n" + - " repository(owner: $owner, name: $repo) {\n" + - ( - reduce $issues[] as $i ([0, ""]; [ - .[0] + 1, - .[1] + ( - " _" + (.[0] | tostring) + - ": issueOrPullRequest(number: " + ($i | tostring) +") {\n" + - " ... on Issue {\n" + - " id\n" + - " }\n" + - " ... on PullRequest {\n"+ - " id\n"+ - " }\n" + - " }\n" - ) - ]) - | .[1] - )+ - " }\n" + - "}" - ' - ) - data=$( - gh api graphql \ - -f owner="$owner" \ - -f repo="$repo" \ - -f query="$query" \ - | jq -c 'reduce .data.repository[].id as $i ([]; . + [$i])' - ) - echo "issue_ids=$data" >> "$GITHUB_OUTPUT" - - - name: Create Missing Labels - id: create_missing - shell: bash - env: - GH_TOKEN: ${{ inputs.gh_token }} - REPO_ID: ${{ steps.collect_labels.outputs.repo_id }} - EXISTING: ${{ steps.collect_labels.outputs.found }} - MISSING: ${{ steps.collect_labels.outputs.missing }} - run: | - query=$( - jq -nr \ - --argjson labels "$MISSING" \ - --arg repo "$REPO_ID" ' - "mutation {\n" + ( - reduce ($labels | keys | .[] | [., $labels[.]]) as $i ([0, ""]; [ - .[0] + 1, - .[1] + ( - " _" + (.[0] | tostring) + - ": createLabel(input: {repositoryId: \"" + $repo + - "\", name: \"" + $i[0] + - "\", color: \"" + $i[1].color + - "\"}) {\n" + - " clientMutationId\n" + - " label {\n" + - " id\n" + - " name\n" + - " color\n" + - " }\n" + - " }\n" - ) - ]) - | .[1] - ) + - "}" - ' - ) - data=$( - gh api graphql -f query="$query" | jq --argjson existing "$EXISTING" ' - reduce .data[].label as $i ({}; .[$i.name] = {"id": $i.id, "color": $i.color }) - | . + $existing - ' - ) - label_ids=$(jq -c '[.[].id]' <<< "$data") - echo "label_ids=$label_ids" >> "$GITHUB_OUTPUT" - - - name: Apply Labels - id: apply_labels - shell: bash - env: - GH_TOKEN: ${{ inputs.gh_token }} - ISSUES: ${{ steps.collect_ids.outputs.issue_ids }} - LABELS: ${{ steps.create_missing.outputs.label_ids }} - run: | - query=$( - jq -nr \ - --argjson labels "$LABELS" \ - --argjson issues "$ISSUES" \ - ' - "mutation {\n" + ( - reduce $issues[] as $i ([0, ""]; [ - .[0] + 1, - .[1] + ( - " _" + (.[0] | tostring) + - ": addLabelsToLabelable(input: {labelableId: \"" + - $i + "\", labelIds: " + ($labels | tojson) + "}) {\n" + - " clientMutationId\n" + - " }\n" - ) - ]) - | .[1] - ) + - "}" - ' - ) - gh api graphql -f query="$query" diff --git a/.github/actions/delete-labels/action.yml b/.github/actions/delete-labels/action.yml deleted file mode 100644 index 65d3ba3d4..000000000 --- a/.github/actions/delete-labels/action.yml +++ /dev/null @@ -1,101 +0,0 @@ -name: Delete Label(s) -description: delete Label(s) -inputs: - gh_token: - description: gh api access token to use - required: true - repository: - description: the OWNER/REPOSITORY to operate on - default: ${{ github.repository }} - labels: - description: a single or comma separated list of labels to delete - required: true - -runs: - using: "composite" - steps: - - name: Collect Repo Labels - id: collect_labels - shell: bash - env: - GH_TOKEN: ${{ inputs.gh_token }} - REPOSITORY: ${{ inputs.repository }} - LABELS: ${{ inputs.labels }} - run: | - owner=$(echo "$REPOSITORY" | cut -d '/' -f 1) - repo=$(echo "$REPOSITORY" | cut -d '/' -f 2) - query=$( - jq -nr \ - --arg labels "$LABELS" \ - ' - (reduce ($labels | split (", *"; null) | .[]) as $i ([]; . + [$i])) as $labels - | "query ($owner: String!, $repo: String!) {\n" + - " repository(owner: $owner, name: $repo) {\n" + - ( - reduce $labels[] as $i ([0, ""]; [ - .[0] + 1, - .[1] + ( - " _" + (.[0] | tostring) + - ": label(name: \"" + $i + "\") {\n" + - " id\n" + - " name\n"+ - " }\n" - ) - ]) - | .[1] - )+ - " }\n" + - "}" - ' - ) - data=$( - gh api graphql \ - -f owner="$owner" \ - -f repo="$repo" \ - -f query="$query" \ - | jq -c \ - --arg labels "$LABELS" \ - --arg colors "$COLORS" \ - --arg defaultColor "$DEFAULT_COLOR" \ - ' - . as $in - | ($labels | split(", *"; null)) as $labels - | ( - reduce ( - $in.data.repository[] - | select( objects | .name as $name | any($labels[]; . == $name ) ) - ) as $i ({}; .[$i.name] = {"id": $i.id}) - ) as $found - | [$found[].id] - ' - ) - echo "label_ids=$data" >> "$GITHUB_OUTPUT" - echo "num_labels=$(jq -r 'length' <<< "$data")" >> "$GITHUB_OUTPUT" - - - name: Delete Labels - id: delete_labels - shell: bash - if: fromJSON( steps.collect_labels.outputs.num_labels ) > 0 - env: - GH_TOKEN: ${{ inputs.gh_token }} - LABELS: ${{ steps.collect_labels.outputs.label_ids }} - run: | - query=$(jq -r ' - . as $in - | ( - "mutation {\n" + ( - reduce $in[] as $id ([0, ""]; [ - .[0] + 1 , - .[1] + ( - " _" + (.[0] | tostring) + ": deleteLabel(input: {id: \"" + $id + "\"}) {\n" + - " clientMutationId\n" + - " }\n" - ) - ]) - | .[1] - ) + - "}" - ) - ' <<< "$LABELS" - ) - gh api graphql -f query="$query" diff --git a/.github/workflows/blocked-prs.yml b/.github/workflows/blocked-prs.yml index 3201e5f7a..21e73660d 100644 --- a/.github/workflows/blocked-prs.yml +++ b/.github/workflows/blocked-prs.yml @@ -60,12 +60,11 @@ jobs: id: dispatch_event_setup env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - REPOSITORY: ${{ github.repository }} PR_NUMBER: ${{ inputs.pr_id }} run: | # setup env for the rest of the workflow - OWNER=$(echo "$REPOSITORY" | cut -d '/' -f 1) - REPO=$(echo "$REPOSITORY" | cut -d '/' -f 2) + OWNER=$(dirname "${{ github.repository }}") + REPO=$(basename "${{ github.repository }}") PR_JSON=$( gh api \ -H "Accept: application/vnd.github.raw+json" \ @@ -155,33 +154,38 @@ jobs: ' done < <(jq -c '.blocking[]' <<< "$BLOCKING_PRS") | jq -c -s ) - blocked_by_labels=$(jq -c 'map( select( .merged | not ) | "blocked-by:" + (.number | tostring))' <<< "$blocked_pr_data" ) echo "data=$blocked_pr_data" >> "$GITHUB_OUTPUT" echo "all_merged=$(jq -r 'all(.[].merged; .)' <<< "$blocked_pr_data")" >> "$GITHUB_OUTPUT" - echo "blocked_by_labels=$blocked_by_labels" >> "$GITHUB_OUTPUT" echo "current_blocking=$(jq -c 'map( select( .merged | not ) | .number )' <<< "$blocked_pr_data" )" >> "$GITHUB_OUTPUT" - - name: Apply Blocked by Labels + - name: Add 'blocked' Label is Missing id: label_blocked - if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 && steps.blocking_data.outputs.blocked_by_labels != '' + if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 && && !contains(fromJSON(env.JOB_DATA).prLabels, 'blocked') && !fromJSON(steps.blocking_data.outputs.all_merged) continue-on-error: true - uses: ./.github/actions/add-labels - with: - repository: ${{ github.repository }} - gh_token: ${{ secrets.GITHUB_TOKEN }} - issues: ${{ env.PR_NUMBER }} - labels: ${{ join( fromJSON(steps.blocking_data.outputs.blocked_by_labels), ',' ) }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh -R ${{ github.repository }} issue edit --add-label 'blocked' $PR_NUMBER - - name: Apply 'blocking:' Label to Unmerged Dependencies + - name: Remove 'blocked' Label if All Dependencies Are Merged + id: unlabel_blocked + if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 && fromJSON(steps.blocking_data.outputs.all_merged) + continue-on-error: true + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh -R ${{ github.repository }} issue edit --remove-label 'blocked' $PR_NUMBER + + - name: Apply 'blocking' Label to Unmerged Dependencies id: label_blocking if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 continue-on-error: true - uses: ./.github/actions/add-labels - with: - repository: ${{ github.repository }} - gh_token: ${{ secrets.GITHUB_TOKEN }} - issues: ${{ join( fromJSON(steps.blocking_data.outputs.current_blocking) , ',' ) }} - labels: ${{ format( 'blocking:{0}', env.PR_NUMBER ) }} + env: + BLOCKING_ISSUES: ${{ steps.blocking_data.outputs.current_blocking }} + run: | + while read -r pr ; do + gh -R ${{ github.repository }} issue edit --add-label 'blocking' "$pr" || true + done < <(jq -c '.[]' <<< "$BLOCKING_ISSUES") - name: Apply Blocking PR Status Check id: blocked_check diff --git a/.github/workflows/manual-add-label.yml b/.github/workflows/manual-add-label.yml deleted file mode 100644 index fdb69e389..000000000 --- a/.github/workflows/manual-add-label.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: "Manual: Apply Labels in Bulk" - -on: - workflow_dispatch: - inputs: - issues: - description: a single or comma separated list of issue numbers - required: true - type: string - labels: - description: a single or comma separated list of labels to apply to all listed issues - required: true - type: string - colors: - description: | - A single or comma separated list of colors to create the labels with if needed. - the list order is the same as `labels`. Missing or blank values (e.g. `FFFFFF,,FFFFFF`) use the `default_color` - type: string - default-color: - description: default color to create labels with - default: "#D4C5F9" - type: string - -jobs: - apply-labels: - name: Apply Labels - runs-on: ubuntu-latest - - permissions: - issues: write - pull-requests: write - - steps: - - name: Checkout Default Branch - uses: actions/checkout@v4 - with: - ref: ${{ github.event.repository.default_branch }} - - name: Run Label Action - uses: ./.github/actions/add-labels - with: - gh_token: ${{ secrets.GITHUB_TOKEN }} - issues: ${{ inputs.issues }} - labels: ${{ inputs.labels }} - colors: ${{ inputs.colors }} - default-color: ${{ inputs.default-color }} diff --git a/.github/workflows/manual-delete-labels.yml b/.github/workflows/manual-delete-labels.yml deleted file mode 100644 index 300c0b52a..000000000 --- a/.github/workflows/manual-delete-labels.yml +++ /dev/null @@ -1,30 +0,0 @@ - -name: "Manual: Delete labels in bulk" - -on: - workflow_dispatch: - inputs: - labels: - description: a single or comma separated list of labels to delete - required: true - type: string - -jobs: - delete-labels: - name: Delete Labels - runs-on: ubuntu-latest - - permissions: - issues: write - pull-requests: write - - steps: - - name: Checkout Default Branch - uses: actions/checkout@v4 - with: - ref: ${{ github.event.repository.default_branch }} - - name: Run Label Action - uses: ./.github/actions/delete-labels - with: - gh_token: ${{ secrets.GITHUB_TOKEN }} - labels: ${{ inputs.labels }} diff --git a/.github/workflows/merge-blocking-pr.yml b/.github/workflows/merge-blocking-pr.yml new file mode 100644 index 000000000..6db3483d4 --- /dev/null +++ b/.github/workflows/merge-blocking-pr.yml @@ -0,0 +1,52 @@ +name: Merged Blocking Pull Request Automation + +on: + pull_request: + types: + - closed + +jobs: + update-blocked-status: + name: Update Blocked Status + runs-on: ubuntu-latest + + # a pr that was a `blocking:` label was merged. + # find the open pr's it was blocked by and trigger a refresh of their state + if: github.event.pull_request.merged == true && contains( join( github.event.pull_request.labels.*.name, ',' ), "blocking" ) + + permissions: + issues: write + pull-requests: write + actions: write + + steps: + - name: Gather Dependent PRs + id: gather_deps + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + blocked_prs=$( + gh -R ${{ github.repository }} pr list --label 'blocked' --json 'number,body' \ + | jq -c --argjson pr "${{ github.event.pull_request.number }}" ' + reduce ( .[] | select( + .body | + scan("(?:blocked (?:by|on)|stacked on):? #([0-9]+)") | + map(tonumber) | + any(.[]; . == $pr) + )) as $i ([]; . + [$i]) + ' + ) + echo "deps=$blocked_prs" >> "$GITHUB_OUTPUT" + echo "numdeps=$(jq -r '. | length' <<< "$blocked_prs")" >> "$GITHUB_OUTPUT" + + - name: Trigger Blocked PR Workflows for Dependants + if: fromJSON(steps.gather_deps.outputs.numdeps) > 0 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DEPS: ${{ steps.gather_deps.outputs.deps }} + run: | + while read -r pr ; do + gh -R ${{ github.repository }} workflow run 'blocked-prs.yml' -r "${{ github.ref_name }}" -f pr_id="$pr" + done < <(jq -c '.[].number' <<< "$DEPS") + diff --git a/.github/workflows/merge-blocking_pr.yml b/.github/workflows/merge-blocking_pr.yml deleted file mode 100644 index a3f40a1a8..000000000 --- a/.github/workflows/merge-blocking_pr.yml +++ /dev/null @@ -1,102 +0,0 @@ -name: Merged Blocking Pull Request Automation - -on: - pull_request: - types: - - closed - -jobs: - update-blocked-status: - name: Update Blocked Status - runs-on: ubuntu-latest - - # a pr that was a `blocking:` label was merged. - # find the open pr's it was blocked by and trigger a refresh of their state - if: github.event.pull_request.merged == true && contains( join( github.event.pull_request.labels.*.name, ',' ), "blocking:" ) - - permissions: - issues: write - pull-requests: write - actions: write - - steps: - - name: Gather Dependent PRs - id: gather_deps - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_NUMBER: ${{ github.event.pull_request.number }} - run: | - owner=$(echo "${{ github.repository }}" | cut -d '/' -f 1) - repo=$(echo "${{ github.repository }}" | cut -d '/' -f 2) - query=" - query(\$repo: String!, \$owner: String!, \$endCursor: String) { - repository(name: \$repo, owner: \$owner) { - pullRequests(first: 100, after: \$endCursor, states: [OPEN], labels: [\"blocked-by:${PR_NUMBER}\"]) { - nodes { - number - bodyText - merged - } - pageInfo { - hasNextPage - endCursor - } - } - } - } - " - blocked_prs=$( - gh api graphql \ - -f repo="$repo" \ - -f owner="$owner" \ - -f query="$query" \ - --paginate \ - --slurp \ - | jq -c --argjson pr "${{ github.event.pull_request.number }}" ' - reduce ( .[].data.repository.pullRequests.nodes[] | select( - .bodyText | - scan("(?:blocked (?:by|on)|stacked on):? #([0-9]+)") | - map(tonumber) | - any(.[]; . == $pr) - )) as $i ([]; . + [$i]) - ' - ) - echo "deps=$blocked_prs" >> "$GITHUB_OUTPUT" - echo "numdeps=$(jq -r '. | length' <<< "$blocked_prs")" >> "$GITHUB_OUTPUT" - - - name: Trigger Blocked PR Workflows for Dependants - if: fromJSON(steps.gather_deps.outputs.numdeps) > 0 - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - DEPS: ${{ steps.gather_deps.outputs.deps }} - run: | - while read -r pr ; do - gh api \ - --method POST \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - "/repos/${{ github.repository }}/actions/workflows/blocked-prs.yml/dispatches" \ - -f "ref=${{ github.ref_name }}" \ - -f "inputs[pr_id]=$pr" - done < <(jq -c '.[].number' <<< "$DEPS") - - label-cleanup: - # this pr is closed, no need for these anymore - name: "Cleanup Related blocking: or blocked-by: labels" - runs-on: ubuntu-latest - - permissions: - issues: write - pull-requests: write - - steps: - - name: Checkout Default Branch - uses: actions/checkout@v4 - with: - ref: ${{ github.event.repository.default_branch }} - - name: Delete Related Labels - uses: ./.github/actions/delete-labels - with: - repository: ${{ github.repository }} - gh_token: ${{ secrets.GITHUB_TOKEN }} - labels: ${{ format('blocking:{0},blocked-by:{0}', github.event.pull_request.number ) }} From e28dd30d87cd9779fc589c51b1519090c3e33ff5 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 14 Mar 2025 19:26:27 -0700 Subject: [PATCH 24/27] ci(blocked_pr): merge env setup for dispatch and pull_request events Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/blocked-prs.yml | 60 +++++++++---------------- .github/workflows/merge-blocking-pr.yml | 2 +- 2 files changed, 23 insertions(+), 39 deletions(-) diff --git a/.github/workflows/blocked-prs.yml b/.github/workflows/blocked-prs.yml index 21e73660d..96a8cfc03 100644 --- a/.github/workflows/blocked-prs.yml +++ b/.github/workflows/blocked-prs.yml @@ -30,13 +30,30 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.event.repository.default_branch }} - - name: Setup From Pull Request Event - if: github.event_name != 'workflow_dispatch' - id: pr_event_setup + + - name: Setup From Dispatch Event + if: github.event_name == 'workflow_dispatch' + id: dispatch_event_setup env: - PR_JSON: "${{ toJSON(github.event.pull_request) }}" + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ inputs.pr_id }} run: | # setup env for the rest of the workflow + OWNER=$(dirname "${{ github.repository }}") + REPO=$(basename "${{ github.repository }}") + 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 "PR_JSON=$PR_JSON" >> "$GITHUB_ENV" + + - name: Setup Environment + id: env_setup + run: | + # setup env for the rest of the workflow + PR_JSON=${PR_JSON:-'${{ toJSON(github.event.pull_request) }}'} { echo "REPO=$(jq -r '.base.repo.name' <<< "$PR_JSON")" echo "OWNER=$(jq -r '.base.repo.owner.login' <<< "$PR_JSON")" @@ -55,39 +72,6 @@ jobs: ' <<< "$PR_JSON")" } >> "$GITHUB_ENV" - - name: Setup From Dispatch Event - if: github.event_name == 'workflow_dispatch' - id: dispatch_event_setup - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_NUMBER: ${{ inputs.pr_id }} - run: | - # setup env for the rest of the workflow - OWNER=$(dirname "${{ github.repository }}") - REPO=$(basename "${{ github.repository }}") - 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 -c ' - { - "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" - name: Find Blocked/Stacked PRs in body id: pr_ids @@ -160,7 +144,7 @@ jobs: - name: Add 'blocked' Label is Missing id: label_blocked - if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 && && !contains(fromJSON(env.JOB_DATA).prLabels, 'blocked') && !fromJSON(steps.blocking_data.outputs.all_merged) + if: (fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0) && !contains(fromJSON(env.JOB_DATA).prLabels, 'blocked') && !fromJSON(steps.blocking_data.outputs.all_merged) continue-on-error: true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/merge-blocking-pr.yml b/.github/workflows/merge-blocking-pr.yml index 6db3483d4..4ac85fa5f 100644 --- a/.github/workflows/merge-blocking-pr.yml +++ b/.github/workflows/merge-blocking-pr.yml @@ -12,7 +12,7 @@ jobs: # a pr that was a `blocking:` label was merged. # find the open pr's it was blocked by and trigger a refresh of their state - if: github.event.pull_request.merged == true && contains( join( github.event.pull_request.labels.*.name, ',' ), "blocking" ) + if: github.event.pull_request.merged == true && contains( join( github.event.pull_request.labels.*.name, ',' ), 'blocking' ) permissions: issues: write From 187728c1f238d55d613796ae6139ffd122c5b80f Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 19 Mar 2025 15:19:13 -0700 Subject: [PATCH 25/27] ci(blocked-pr): use app token Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/blocked-prs.yml | 25 +++++++++++++------------ .github/workflows/merge-blocking-pr.yml | 16 +++++++++------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/.github/workflows/blocked-prs.yml b/.github/workflows/blocked-prs.yml index 96a8cfc03..4c1840b01 100644 --- a/.github/workflows/blocked-prs.yml +++ b/.github/workflows/blocked-prs.yml @@ -19,13 +19,14 @@ jobs: name: Check Blocked Status runs-on: ubuntu-latest - permissions: - issues: write - pull-requests: write - statuses: write - checks: write - steps: + - name: Generate token + id: generate-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ vars.PULL_REQUEST_APP_ID }} + private-key: ${{ secrets.PULL_REQUEST_APP_PRIVATE_KEY }} + - name: Checkout Default Branch uses: actions/checkout@v4 with: @@ -35,7 +36,7 @@ jobs: if: github.event_name == 'workflow_dispatch' id: dispatch_event_setup env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ steps.generate-token.outputs.token }} PR_NUMBER: ${{ inputs.pr_id }} run: | # setup env for the rest of the workflow @@ -112,7 +113,7 @@ jobs: id: blocking_data if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ steps.generate-token.outputs.token }} BLOCKING_PRS: ${{ steps.pr_ids.outputs.prs }} run: | blocked_pr_data=$( @@ -147,7 +148,7 @@ jobs: if: (fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0) && !contains(fromJSON(env.JOB_DATA).prLabels, 'blocked') && !fromJSON(steps.blocking_data.outputs.all_merged) continue-on-error: true env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ steps.generate-token.outputs.token }} run: | gh -R ${{ github.repository }} issue edit --add-label 'blocked' $PR_NUMBER @@ -156,7 +157,7 @@ jobs: if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 && fromJSON(steps.blocking_data.outputs.all_merged) continue-on-error: true env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ steps.generate-token.outputs.token }} run: | gh -R ${{ github.repository }} issue edit --remove-label 'blocked' $PR_NUMBER @@ -176,7 +177,7 @@ jobs: if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 continue-on-error: true env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ steps.generate-token.outputs.token }} BLOCKING_DATA: ${{ steps.blocking_data.outputs.data }} run: | pr_head_sha=$(jq -r '.prHeadSha' <<< "$JOB_DATA") @@ -229,5 +230,5 @@ jobs: comment_id: "block_pr_dependencies" issue_number: ${{ env.PR_NUMBER }} repository: ${{ github.repository }} - gh_token: ${{ secrets.GITHUB_TOKEN }} + gh_token: ${{ steps.generate-token.outputs.token }} diff --git a/.github/workflows/merge-blocking-pr.yml b/.github/workflows/merge-blocking-pr.yml index 4ac85fa5f..755cdcde9 100644 --- a/.github/workflows/merge-blocking-pr.yml +++ b/.github/workflows/merge-blocking-pr.yml @@ -14,16 +14,18 @@ jobs: # find the open pr's it was blocked by and trigger a refresh of their state if: github.event.pull_request.merged == true && contains( join( github.event.pull_request.labels.*.name, ',' ), 'blocking' ) - permissions: - issues: write - pull-requests: write - actions: write - steps: + - name: Generate token + id: generate-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ vars.PULL_REQUEST_APP_ID }} + private-key: ${{ secrets.PULL_REQUEST_APP_PRIVATE_KEY }} + - name: Gather Dependent PRs id: gather_deps env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ steps.generate-token.outputs.token }} PR_NUMBER: ${{ github.event.pull_request.number }} run: | blocked_prs=$( @@ -43,7 +45,7 @@ jobs: - name: Trigger Blocked PR Workflows for Dependants if: fromJSON(steps.gather_deps.outputs.numdeps) > 0 env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ steps.generate-token.outputs.token }} DEPS: ${{ steps.gather_deps.outputs.deps }} run: | while read -r pr ; do From 436896d36524c6c625637c74bc5c1174c04eed58 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 19 Mar 2025 15:30:26 -0700 Subject: [PATCH 26/27] ci(blocked-pr): use `gh issue comment --create-if-none --edit-last` with app token, not composit action Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/actions/create-comment/action.yml | 155 ---------------------- .github/workflows/blocked-prs.yml | 31 ++--- 2 files changed, 14 insertions(+), 172 deletions(-) delete mode 100644 .github/actions/create-comment/action.yml diff --git a/.github/actions/create-comment/action.yml b/.github/actions/create-comment/action.yml deleted file mode 100644 index 484f1e5af..000000000 --- a/.github/actions/create-comment/action.yml +++ /dev/null @@ -1,155 +0,0 @@ -name: Create Issue Comment -description: Create or update an issue comment -inputs: - comment: - description: Comment Text - required: true - comment_path: - description: "Path to txt file to be parsed" - required: false - comment_id: - description: "Unique identifier for deduplicating comments" - default: "Create Issue Action" - required: false - issue_number: - description: Local Pull Request/Issue number to work on - required: true - gh_token: - description: gh api access token to use - required: true - repository: - description: the OWNER/REPOSITORY to operate on - default: ${{ github.repository }} - -runs: - using: "composite" - steps: - - name: Generate Comment Text - shell: bash - env: - COMMENT_ID: ${{ inputs.comment_id }} - COMMENT_TEXT: ${{ inputs.comment }} - COMMENT_FILE: ${{ inputs.comment_path }} - run: | - comment_body="${COMMENT_TEXT}" - if [ -f "$COMMENT_FILE" ] ; then - echo "Reading comment file from ${COMMENT_FILE}" - comment_body="${comment_body}$(cat "$COMMENT_FILE")" - fi - echo 'COMMENT_BODY<> "$GITHUB_ENV" - echo "$comment_body" >> "$GITHUB_ENV" - echo 'EOF' >> "$GITHUB_ENV" - - - name: Get Existing Comment Id - shell: bash - env: - GH_TOKEN: ${{ inputs.gh_token }} - ISSUE_NUMBER: ${{ inputs.issue_number }} - REPOSITORY: ${{ inputs.repository }} - COMMENT_ID: ${{ inputs.comment_id }} - run: | - owner=$(echo "$REPOSITORY" | cut -d '/' -f 1) - repo=$(echo "$REPOSITORY" | cut -d '/' -f 2) - data=$( - gh api graphql \ - --paginate \ - --slurp \ - -f owner="$owner" \ - -f repo="$repo" \ - -F issue="$ISSUE_NUMBER" \ - -f query=' - query($repo: String!, $owner: String!, $issue: Int!, $endCursor: String) { - repository(name: $repo, owner: $owner) { - issueOrPullRequest(number: $issue) { - ... on Issue { - id - number - comments(first: 100, after: $endCursor) { - nodes { - id - body - } - pageInfo { - hasNextPage - endCursor - } - } - } - ... on PullRequest { - id - number - comments(first: 100, after: $endCursor) { - nodes { - id - body - } - pageInfo { - hasNextPage - endCursor - } - } - } - } - } - } - ' \ - | jq -c --arg comment_id "" ' - .[0].data.repository.issueOrPullRequest.id as $id | - [ .[].data.repository.issueOrPullRequest.comments.nodes[] ] as $data | - [ $data.[] | select(.body | startswith($comment_id)) ] as $id_comments | - if ($id_comments | length) > 0 then - { "issueId": $id, "commentId": $id_comments[0].id } - else - { "issueId": $id, "commentId": "" } - end - ' - ) - echo "ISSUE_NODE_ID=$(jq -r '.issueId' <<< "$data")" >> "$GITHUB_ENV" - echo "COMMENT_NODE_ID=$(jq -r '.commentId' <<< "$data")" >> "$GITHUB_ENV" - - - name: Edit Existing Comment - if: env.COMMENT_NODE_ID != '' - shell: bash - env: - GH_TOKEN: ${{ inputs.gh_token }} - run: | - gh api graphql \ - -f comment_id="$COMMENT_NODE_ID" \ - -f comment_body="$COMMENT_BODY" \ - -f query=' - mutation($comment_id: ID!, $comment_body: String!) { - updateIssueComment(input: { - id: $comment_id, - body: $comment_body, - }) { - issueComment { - lastEditedAt - } - } - } - ' - - - name: Create Comment - if: env.COMMENT_NODE_ID == '' - shell: bash - env: - GH_TOKEN: ${{ inputs.gh_token }} - run: | - gh api graphql \ - -f issue_id="$ISSUE_NODE_ID" \ - -f comment_body="$COMMENT_BODY" \ - -f query=' - mutation ($issue_id: ID!, $comment_body: String!) { - addComment(input: { subjectId: $issue_id, body: $comment_body }) { - commentEdge { - node { - id - } - } - } - } - ' - - - - diff --git a/.github/workflows/blocked-prs.yml b/.github/workflows/blocked-prs.yml index 4c1840b01..9a6caab7d 100644 --- a/.github/workflows/blocked-prs.yml +++ b/.github/workflows/blocked-prs.yml @@ -27,11 +27,6 @@ jobs: app-id: ${{ vars.PULL_REQUEST_APP_ID }} private-key: ${{ secrets.PULL_REQUEST_APP_PRIVATE_KEY }} - - name: Checkout Default Branch - uses: actions/checkout@v4 - with: - ref: ${{ github.event.repository.default_branch }} - - name: Setup From Dispatch Event if: github.event_name == 'workflow_dispatch' id: dispatch_event_setup @@ -198,15 +193,14 @@ jobs: done < <(jq -c '.[]' <<< "$BLOCKING_DATA") - name: Context Comment - id: blocked_comment + id: generate-comment if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 continue-on-error: true env: BLOCKING_DATA: ${{ steps.blocking_data.outputs.data }} run: | COMMENT_PATH="$(pwd)/temp_comment_file.txt" - touch "$COMMENT_PATH" - echo "" > "$COMMENT_PATH" + echo '

PR Dependencies :pushpin:

' > "$COMMENT_PATH" pr_head_label=$(jq -r '.prHeadLabel' <<< "$JOB_DATA") while read -r pr_data ; do base_pr=$(jq -r '.number' <<< "$pr_data") @@ -218,17 +212,20 @@ jobs: type=$(jq -r '.type' <<< "$pr_data") echo " - $type #$base_pr $status [(compare)]($compare_url)" >> "$COMMENT_PATH" done < <(jq -c '.[]' <<< "$BLOCKING_DATA") - echo "file_path=${COMMENT_PATH}" >> "$GITHUB_OUTPUT" + + echo 'body<> "$GITHUB_OUTPUT" + cat "${COMMENT_PATH}" >> "$GITHUB_OUTPUT" + echo 'EOF' >> "$GITHUB_OUTPUT" - name: 💬 PR Comment if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 continue-on-error: true - uses: ./.github/actions/create-comment - with: - comment: "

PR Dependencies :pushpin:

" - comment_path: ${{ steps.blocked_comment.outputs.file_path }} - comment_id: "block_pr_dependencies" - issue_number: ${{ env.PR_NUMBER }} - repository: ${{ github.repository }} - gh_token: ${{ steps.generate-token.outputs.token }} + env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} + COMMENT_BODY: ${{ steps.generate-comment.outputs.body }} + run: | + gh -R ${{ github.repository }} issue comment "$PR_NUMBER" \ + --body "$COMMENT_BODY" \ + --create-if-none \ + --edit-last From 815306f1d0e1155f27a650ea7bfbee89a857c0de Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 21 Mar 2025 12:16:44 -0700 Subject: [PATCH 27/27] Use group redirections for multiple outputs, prevent glob expansion of PR_NUMBER Co-authored-by: Seth Flynn Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/blocked-prs.yml | 20 ++++++++++++-------- .github/workflows/merge-blocking-pr.yml | 6 ++++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/.github/workflows/blocked-prs.yml b/.github/workflows/blocked-prs.yml index 9a6caab7d..d32f53bb1 100644 --- a/.github/workflows/blocked-prs.yml +++ b/.github/workflows/blocked-prs.yml @@ -134,9 +134,11 @@ jobs: ' done < <(jq -c '.blocking[]' <<< "$BLOCKING_PRS") | jq -c -s ) - echo "data=$blocked_pr_data" >> "$GITHUB_OUTPUT" - echo "all_merged=$(jq -r 'all(.[].merged; .)' <<< "$blocked_pr_data")" >> "$GITHUB_OUTPUT" - echo "current_blocking=$(jq -c 'map( select( .merged | not ) | .number )' <<< "$blocked_pr_data" )" >> "$GITHUB_OUTPUT" + { + echo "data=$blocked_pr_data"; + echo "all_merged=$(jq -r 'all(.[].merged; .)' <<< "$blocked_pr_data")"; + echo "current_blocking=$(jq -c 'map( select( .merged | not ) | .number )' <<< "$blocked_pr_data" )"; + } >> "$GITHUB_OUTPUT" - name: Add 'blocked' Label is Missing id: label_blocked @@ -145,7 +147,7 @@ jobs: env: GH_TOKEN: ${{ steps.generate-token.outputs.token }} run: | - gh -R ${{ github.repository }} issue edit --add-label 'blocked' $PR_NUMBER + gh -R ${{ github.repository }} issue edit --add-label 'blocked' "$PR_NUMBER" - name: Remove 'blocked' Label if All Dependencies Are Merged id: unlabel_blocked @@ -154,7 +156,7 @@ jobs: env: GH_TOKEN: ${{ steps.generate-token.outputs.token }} run: | - gh -R ${{ github.repository }} issue edit --remove-label 'blocked' $PR_NUMBER + gh -R ${{ github.repository }} issue edit --remove-label 'blocked' "$PR_NUMBER" - name: Apply 'blocking' Label to Unmerged Dependencies id: label_blocking @@ -213,9 +215,11 @@ jobs: echo " - $type #$base_pr $status [(compare)]($compare_url)" >> "$COMMENT_PATH" done < <(jq -c '.[]' <<< "$BLOCKING_DATA") - echo 'body<> "$GITHUB_OUTPUT" - cat "${COMMENT_PATH}" >> "$GITHUB_OUTPUT" - echo 'EOF' >> "$GITHUB_OUTPUT" + { + echo 'body<> "$GITHUB_OUTPUT" - name: 💬 PR Comment if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 diff --git a/.github/workflows/merge-blocking-pr.yml b/.github/workflows/merge-blocking-pr.yml index 755cdcde9..6f85b9282 100644 --- a/.github/workflows/merge-blocking-pr.yml +++ b/.github/workflows/merge-blocking-pr.yml @@ -39,8 +39,10 @@ jobs: )) as $i ([]; . + [$i]) ' ) - echo "deps=$blocked_prs" >> "$GITHUB_OUTPUT" - echo "numdeps=$(jq -r '. | length' <<< "$blocked_prs")" >> "$GITHUB_OUTPUT" + { + echo "deps=$blocked_prs" + echo "numdeps=$(jq -r '. | length' <<< "$blocked_prs")" + } >> "$GITHUB_OUTPUT" - name: Trigger Blocked PR Workflows for Dependants if: fromJSON(steps.gather_deps.outputs.numdeps) > 0