From eef63a3b79ec7e8db4da709fd7e4aa975424d145 Mon Sep 17 00:00:00 2001 From: "KH." <142028490+YanagiKH@users.noreply.github.com> Date: Tue, 1 Sep 2026 15:50:21 +0900 Subject: [PATCH 1/2] fix: bind release gates to trusted workflows --- .github/workflows/release.yml | 98 +++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bad510d..e3cb08b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -77,10 +77,42 @@ 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="$( + gh api --paginate --slurp \ + "repos/${GITHUB_REPOSITORY}/actions/runs?head_sha=${RELEASE_SHA}&branch=main&event=push&per_page=100" | + jq -c --arg sha "$RELEASE_SHA" --arg repository "$GITHUB_REPOSITORY" ' + [.[] | .workflow_runs[] | + select( + .repository.full_name == $repository and + .event == "push" and + .head_branch == "main" and + .head_sha == $sha and + (.path == ".github/workflows/ci.yml" or + .path == ".github/workflows/security.yml") + ) | .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 +350,39 @@ 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="$( + gh api --paginate --slurp \ + "repos/${GITHUB_REPOSITORY}/actions/runs?head_sha=${RELEASE_SHA}&branch=main&event=push&per_page=100" | + jq -c --arg sha "$RELEASE_SHA" --arg repository "$GITHUB_REPOSITORY" ' + [.[] | .workflow_runs[] | + select( + .repository.full_name == $repository and + .event == "push" and + .head_branch == "main" and + .head_sha == $sha and + (.path == ".github/workflows/ci.yml" or + .path == ".github/workflows/security.yml") + ) | .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 +697,39 @@ 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="$( + gh api --paginate --slurp \ + "repos/${GITHUB_REPOSITORY}/actions/runs?head_sha=${RELEASE_SHA}&branch=main&event=push&per_page=100" | + jq -c --arg sha "$RELEASE_SHA" --arg repository "$GITHUB_REPOSITORY" ' + [.[] | .workflow_runs[] | + select( + .repository.full_name == $repository and + .event == "push" and + .head_branch == "main" and + .head_sha == $sha and + (.path == ".github/workflows/ci.yml" or + .path == ".github/workflows/security.yml") + ) | .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 From 263ff5a388fc234b7ff9c87378bf3c35712ce45e Mon Sep 17 00:00:00 2001 From: "KH." <142028490+YanagiKH@users.noreply.github.com> Date: Tue, 1 Sep 2026 15:55:16 +0900 Subject: [PATCH 2/2] fix: grant and resolve trusted action runs --- .github/workflows/release.yml | 46 +++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e3cb08b..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: @@ -80,17 +81,18 @@ jobs: check_runs_file="$RUNNER_TEMP/disrunner-main-check-runs.json" for attempt in $(seq 1 60); do main_suite_ids="$( - gh api --paginate --slurp \ - "repos/${GITHUB_REPOSITORY}/actions/runs?head_sha=${RELEASE_SHA}&branch=main&event=push&per_page=100" | - jq -c --arg sha "$RELEASE_SHA" --arg repository "$GITHUB_REPOSITORY" ' - [.[] | .workflow_runs[] | + 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 and - (.path == ".github/workflows/ci.yml" or - .path == ".github/workflows/security.yml") + .head_sha == $sha ) | .check_suite_id] | unique ' )" @@ -351,17 +353,18 @@ jobs: 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="$( - gh api --paginate --slurp \ - "repos/${GITHUB_REPOSITORY}/actions/runs?head_sha=${RELEASE_SHA}&branch=main&event=push&per_page=100" | - jq -c --arg sha "$RELEASE_SHA" --arg repository "$GITHUB_REPOSITORY" ' - [.[] | .workflow_runs[] | + 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 and - (.path == ".github/workflows/ci.yml" or - .path == ".github/workflows/security.yml") + .head_sha == $sha ) | .check_suite_id] | unique ' )" @@ -698,17 +701,18 @@ 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="$( - gh api --paginate --slurp \ - "repos/${GITHUB_REPOSITORY}/actions/runs?head_sha=${RELEASE_SHA}&branch=main&event=push&per_page=100" | - jq -c --arg sha "$RELEASE_SHA" --arg repository "$GITHUB_REPOSITORY" ' - [.[] | .workflow_runs[] | + 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 and - (.path == ".github/workflows/ci.yml" or - .path == ".github/workflows/security.yml") + .head_sha == $sha ) | .check_suite_id] | unique ' )"