diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bad510d..6b96a98 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,6 +30,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 35 permissions: + actions: read checks: read contents: read outputs: @@ -77,10 +78,43 @@ jobs: run: | set -euo pipefail REQUIRED=("All green" "CodeQL" "Code scanning high-severity policy" "pnpm audit and security suite") + check_runs_file="$RUNNER_TEMP/disrunner-main-check-runs.json" for attempt in $(seq 1 60); do + main_suite_ids="$( + for workflow in ci.yml security.yml; do + gh api --paginate \ + "repos/${GITHUB_REPOSITORY}/actions/workflows/${workflow}/runs?head_sha=${RELEASE_SHA}&branch=main&event=push&per_page=100" \ + --jq '.workflow_runs[]' + done | + jq -sc --arg sha "$RELEASE_SHA" --arg repository "$GITHUB_REPOSITORY" ' + [.[] | + select( + .repository.full_name == $repository and + .event == "push" and + .head_branch == "main" and + .head_sha == $sha + ) | .check_suite_id] | unique + ' + )" + test "$main_suite_ids" != '[]' || { + echo "No GitHub Actions check suite is bound to main at $RELEASE_SHA yet." + } + gh api --paginate --slurp \ + "repos/${GITHUB_REPOSITORY}/commits/${RELEASE_SHA}/check-runs?per_page=100" \ + > "$check_runs_file" all_green=true for check in "${REQUIRED[@]}"; do - conclusion="$(gh api "repos/${GITHUB_REPOSITORY}/commits/${RELEASE_SHA}/check-runs?per_page=100" --jq ".check_runs | map(select(.name == \"${check}\" and .app.slug == \"github-actions\" and .check_suite.head_branch == \"main\")) | sort_by(.started_at) | last | (.conclusion // \"pending\")")" + conclusion="$( + jq -r --arg check "$check" --argjson suite_ids "$main_suite_ids" ' + [.[] | .check_runs[] | + select( + .name == $check and + .app.slug == "github-actions" and + (.check_suite.id as $id | $suite_ids | index($id)) + )] | + sort_by(.started_at) | last | (.conclusion // "pending") + ' "$check_runs_file" + )" echo "${check}: ${conclusion}" case "$conclusion" in success) ;; @@ -318,9 +352,40 @@ jobs: git fetch --force origin "refs/tags/$RELEASE_TAG:refs/tags/disrunner-release-verify" test "$(git cat-file -t refs/tags/disrunner-release-verify)" = tag test "$(git rev-parse 'refs/tags/disrunner-release-verify^{commit}')" = "$RELEASE_SHA" + main_suite_ids="$( + for workflow in ci.yml security.yml; do + gh api --paginate \ + "repos/${GITHUB_REPOSITORY}/actions/workflows/${workflow}/runs?head_sha=${RELEASE_SHA}&branch=main&event=push&per_page=100" \ + --jq '.workflow_runs[]' + done | + jq -sc --arg sha "$RELEASE_SHA" --arg repository "$GITHUB_REPOSITORY" ' + [.[] | + select( + .repository.full_name == $repository and + .event == "push" and + .head_branch == "main" and + .head_sha == $sha + ) | .check_suite_id] | unique + ' + )" + test "$main_suite_ids" != '[]' + check_runs_file="$RUNNER_TEMP/disrunner-final-main-check-runs.json" + gh api --paginate --slurp \ + "repos/${GITHUB_REPOSITORY}/commits/${RELEASE_SHA}/check-runs?per_page=100" \ + > "$check_runs_file" REQUIRED=("All green" "CodeQL" "Code scanning high-severity policy" "pnpm audit and security suite") for check in "${REQUIRED[@]}"; do - conclusion="$(gh api "repos/${GITHUB_REPOSITORY}/commits/${RELEASE_SHA}/check-runs?per_page=100" --jq ".check_runs | map(select(.name == \"${check}\" and .app.slug == \"github-actions\" and .check_suite.head_branch == \"main\")) | sort_by(.started_at) | last | (.conclusion // \"missing\")")" + conclusion="$( + jq -r --arg check "$check" --argjson suite_ids "$main_suite_ids" ' + [.[] | .check_runs[] | + select( + .name == $check and + .app.slug == "github-actions" and + (.check_suite.id as $id | $suite_ids | index($id)) + )] | + sort_by(.started_at) | last | (.conclusion // "missing") + ' "$check_runs_file" + )" test "$conclusion" = success || { echo "Final required check is not green: ${check} (${conclusion})." >&2 exit 1 @@ -635,9 +700,40 @@ jobs: git fetch --force --no-tags origin '+refs/heads/main:refs/remotes/origin/main' test "$(git rev-parse refs/remotes/origin/main)" = "$RELEASE_SHA" + main_suite_ids="$( + for workflow in ci.yml security.yml; do + gh api --paginate \ + "repos/${GITHUB_REPOSITORY}/actions/workflows/${workflow}/runs?head_sha=${RELEASE_SHA}&branch=main&event=push&per_page=100" \ + --jq '.workflow_runs[]' + done | + jq -sc --arg sha "$RELEASE_SHA" --arg repository "$GITHUB_REPOSITORY" ' + [.[] | + select( + .repository.full_name == $repository and + .event == "push" and + .head_branch == "main" and + .head_sha == $sha + ) | .check_suite_id] | unique + ' + )" + test "$main_suite_ids" != '[]' + check_runs_file="$RUNNER_TEMP/disrunner-publish-main-check-runs.json" + gh api --paginate --slurp \ + "repos/${GITHUB_REPOSITORY}/commits/${RELEASE_SHA}/check-runs?per_page=100" \ + > "$check_runs_file" REQUIRED=("All green" "CodeQL" "Code scanning high-severity policy" "pnpm audit and security suite") for check in "${REQUIRED[@]}"; do - conclusion="$(gh api "repos/${GITHUB_REPOSITORY}/commits/${RELEASE_SHA}/check-runs?per_page=100" --jq ".check_runs | map(select(.name == \"${check}\" and .app.slug == \"github-actions\" and .check_suite.head_branch == \"main\")) | sort_by(.started_at) | last | (.conclusion // \"missing\")")" + conclusion="$( + jq -r --arg check "$check" --argjson suite_ids "$main_suite_ids" ' + [.[] | .check_runs[] | + select( + .name == $check and + .app.slug == "github-actions" and + (.check_suite.id as $id | $suite_ids | index($id)) + )] | + sort_by(.started_at) | last | (.conclusion // "missing") + ' "$check_runs_file" + )" test "$conclusion" = success || { echo "Final required check is not green: ${check} (${conclusion})." >&2 exit 1