GitHub Actionsで大量に詰まったQueueを一括キャンセルするシェルスクリプト

Tadashi Shigeoka ·  Sun, July 13, 2025

CI/CDの運用をしていると、時として予期せぬ事態が発生します。例えば、GitHub Actionsのワークフロー設定を少し間違えただけで、大量のジョブがQueueに溜まってしまうことがあります。

GitHubのWeb UIから一つずつキャンセルするのは、あまりにも非現実的で骨の折れる作業です。

今回は、そんな絶望的な状況を打開するための、Queueに溜まったワークフローを一括でキャンセルするシェルスクリプトを紹介します。gh (GitHub CLI) を活用し、並列処理で高速にキャンセルを実行します。

こんなときに便利

  • ワークフローのトリガー設定(例: on: push)を間違えて、大量のジョブがQueueに入ってしまった。
  • 依存関係の更新などで多数のリポジトリに一斉にPull Requestを作成し、CIが詰まってしまった。
  • 特定のワークフローを緊急で停止させたいが、すでに大量のジョブがスケジュールされてしまっている。

一括キャンセルスクリプト

早速ですが、こちらがそのスクリプトです。リポジトリを指定するだけで、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}"

使い方

1. 前提条件

このスクリプトは GitHub CLI (gh) を使用します。事前にインストールし、認証を済ませておいてください。

# インストール (macOS)
brew install gh
 
# 認証
gh auth login

2. スクリプトの実行

  1. 上記のスクリプトを gh_cancel_queued_workflows.sh などのファイル名で保存します。
  2. 実行権限を付与します。
    chmod +x gh_cancel_queued_workflows.sh
  3. 引数にターゲットのリポジトリ名を <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を一括キャンセルした、現場からお送りしました。

参考情報