CI/CDの運用をしていると、時として予期せぬ事態が発生します。例えば、GitHub Actionsのワークフロー設定を少し間違えただけで、大量のジョブがQueueに溜まってしまうことがあります。
GitHubのWeb UIから一つずつキャンセルするのは、あまりにも非現実的で骨の折れる作業です。
今回は、そんな絶望的な状況を打開するための、Queueに溜まったワークフローを一括でキャンセルするシェルスクリプトを紹介します。gh
(GitHub CLI) を活用し、並列処理で高速にキャンセルを実行します。
on: push
)を間違えて、大量のジョブがQueueに入ってしまった。早速ですが、こちらがそのスクリプトです。リポジトリを指定するだけで、queued
状態のワークフローをすべてキャンセルします。
#!/bin/bash
# GitHub Actions Workflow Queue Cancellation Script
# This script cancels all queued workflows in the repository
#
# Usage: ./gh_cancel_queued_workflows.sh <owner/repo>
# Example: ./gh_cancel_queued_workflows.sh codenote-net/sandbox
# Check arguments
if [ $# -lt 1 ]; then
echo "Error: Missing required argument"
echo "Usage: $0 <owner/repo>"
echo "Example: $0 codenote-net/sandbox"
exit 1
fi
REPOSITORY="$1"
# Validate repository format
if [[ ! "$REPOSITORY" =~ ^[^/]+/[^/]+$ ]]; then
echo "Error: Invalid repository format"
echo "Expected format: owner/repo"
echo "Example: codenote-net/sandbox"
exit 1
fi
PER_PAGE=100
PARALLEL_JOBS=10
TOTAL_CANCELLED=0
TOTAL_ERRORS=0
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo -e "${GREEN}Starting workflow cancellation for ${REPOSITORY}${NC}"
echo "This may take a while for ~100,000 workflows..."
# Verify repository exists and we have access
echo "Verifying repository access..."
if ! gh api "/repos/${REPOSITORY}" >/dev/null 2>&1; then
echo -e "${RED}Error: Cannot access repository ${REPOSITORY}${NC}"
echo "Please check:"
echo " - Repository name and owner are correct"
echo " - You are authenticated with gh CLI"
echo " - You have necessary permissions"
exit 1
fi
# Function to cancel a single workflow
cancel_workflow() {
local run_id=$1
if gh api -X POST "/repos/${REPOSITORY}/actions/runs/${run_id}/cancel" >/dev/null 2>&1; then
echo -e "${GREEN}✓${NC} Cancelled workflow: ${run_id}"
return 0
else
echo -e "${RED}✗${NC} Failed to cancel workflow: ${run_id}"
return 1
fi
}
# Export function for parallel execution
export -f cancel_workflow
export REPOSITORY RED GREEN NC
# Function to process workflows in parallel
process_page() {
local page=$1
# Get queued workflows for this page
local workflows=$(gh api \
"/repos/${REPOSITORY}/actions/runs?status=queued&per_page=${PER_PAGE}&page=${page}" \
--jq '.workflow_runs[].id' 2>/dev/null)
if [ -z "$workflows" ]; then
return 1
fi
# Process workflows in parallel
echo "$workflows" | xargs -P ${PARALLEL_JOBS} -I {} bash -c 'cancel_workflow {}'
return 0
}
# Get total count of queued workflows
echo "Fetching total count of queued workflows..."
TOTAL_COUNT=$(gh api "/repos/${REPOSITORY}/actions/runs?status=queued&per_page=1" --jq '.total_count' 2>/dev/null)
if [ -z "$TOTAL_COUNT" ] || [ "$TOTAL_COUNT" -eq 0 ]; then
echo -e "${YELLOW}No queued workflows found.${NC}"
exit 0
fi
echo -e "${YELLOW}Found ${TOTAL_COUNT} queued workflows${NC}"
TOTAL_PAGES=$((($TOTAL_COUNT + $PER_PAGE - 1) / $PER_PAGE))
# Process all pages
echo "Processing ${TOTAL_PAGES} pages..."
for ((page=1; page<=TOTAL_PAGES; page++)); do
echo -e "\n${YELLOW}Processing page ${page}/${TOTAL_PAGES}${NC}"
if ! process_page $page; then
echo "No more workflows found on page ${page}"
break
fi
# Add a small delay to avoid rate limiting
if [ $((page % 10)) -eq 0 ]; then
echo "Pausing briefly to avoid rate limits..."
sleep 2
fi
done
echo -e "\n${GREEN}Workflow cancellation completed!${NC}"
このスクリプトは GitHub CLI (gh
) を使用します。事前にインストールし、認証を済ませておいてください。
# インストール (macOS)
brew install gh
# 認証
gh auth login
gh_cancel_queued_workflows.sh
などのファイル名で保存します。chmod +x gh_cancel_queued_workflows.sh
<owner>/<repo>
の形式で指定して実行します。
./gh_cancel_queued_workflows.sh your-org/your-repo
実行すると、対象リポジトリのQueueにあるワークフローを自動で取得し、並列でキャンセル処理を開始します。
このスクリプトはいくつかのテクニックを組み合わせて、効率的な処理を実現しています。
gh api
によるAPIアクセスgh
コマンドには gh api
というサブコマンドがあり、これを使うとGitHub APIを簡単に呼び出すことができます。認証情報を自動で付与してくれるため、curl
などで自前でヘッダーを設定する必要がありません。
ここでは、ワークフローの一覧取得とキャンセルAPIの呼び出しに使用しています。
/repos/{owner}/{repo}/actions/runs?status=queued
POST /repos/{owner}/{repo}/actions/runs/{run_id}/cancel
GitHub APIは一度に取得できる件数に上限があるため(デフォルト30、最大100)、大量のワークフローに対応するにはページネーション処理が必須です。
スクリプトでは、まず全体の件数を取得し、必要なページ数を計算。その後、for
ループで全ページを順番に処理しています。per_page=100
を指定して、一度のリクエストで最大件数を取得するようにしています。
xargs
による並列処理取得したワークフローIDを一つずつ順番にキャンセルしていては、時間がかかりすぎてしまいます。そこで xargs
コマンドの -P
(または --max-procs
) オプションを使い、キャンセル処理を並列化しています。
# ...
PARALLEL_JOBS=10
# ...
echo "$workflows" | xargs -P ${PARALLEL_JOBS} -I {} bash -c 'cancel_workflow {}'
PARALLEL_JOBS
で並列数を指定しており、ここでは10個ずつ同時にキャンセル処理が実行されます。これにより、処理時間を劇的に短縮できます。
スクリプトの冒頭で引数やリポジトリへのアクセス権をチェックしたり、処理の進捗が分かるように色付きのログを出力したりすることで、利用者が安心して使えるように工夫しています。
gh
CLIと少しのシェルスクリプトを組み合わせることで、手作業では途方に暮れるような定型業務を自動化し、大幅に効率化できます。
GitHub Actionsの運用で困った際には、ぜひこのスクリプトを試してみてください。また、これをベースに自分の用途に合わせてカスタマイズするのも良いでしょう。例えば、status=queued
の部分を status=in_progress
に変更すれば、実行中のワークフローを対象にすることも可能です(実行には注意が必要ですが)。
gh
は、今回紹介したAPI操作以外にも豊富な機能を備えた強力なツールです。まだ使ったことがない方は、この機会に触れてみることを強くお勧めします。
以上、GitHub Actionsで大量に詰まったQueueを一括キャンセルした、現場からお送りしました。