Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/Morrison-Lab/ai-config --skill cancel-superseded명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
| name | cancel-superseded |
| description | Cancel superseded CI pipelines. |
| user-invocable | true |
| allowed-tools | ["Bash"] |
Cancel older pipelines on the same branch that have been superseded by a newer push, freeing CI runners for the latest pipeline.
GitLab-only. This skill drives
glabagainst GitLab CI pipelines. There is no GitHub equivalent here — on a GitHub repo, cancel superseded runs withgh run cancel <run-id>instead (or rely on per-PRconcurrencyin the workflow, which auto-cancels superseded runs).
pending$PROJECT_ID instead of a manual placeholder):PROJECT_ID="$(glab api "projects?search=$(basename "$(git rev-parse --show-toplevel)")" 2>/dev/null | \
python3 -c "import json,sys; print(json.load(sys.stdin)[0]['id'])")"
echo "PROJECT_ID=$PROJECT_ID"
If the search returns more than one project (same repo name in different
groups), set PROJECT_ID by hand from glab api "projects?search=<name>".
BRANCH=$(git branch --show-current)
# Get pipelines for this branch, newest first
glab api "projects/$PROJECT_ID/pipelines?ref=$BRANCH&sort=desc&per_page=10" | \
python3 -c "
import json, sys
try: pipelines = json.load(sys.stdin)
except json.JSONDecodeError: sys.exit(1) # glab error already on stderr; don't add a traceback
for p in pipelines:
print(f'{p[\"id\"]:>6} {p[\"status\"]:12s} {p[\"ref\"]}')
" | cat
running/pending/created is a
cancel candidate. This step only prints — nothing is canceled yet:glab api "projects/$PROJECT_ID/pipelines?ref=$BRANCH&sort=desc&per_page=10" | \
python3 -c "
import json, sys
try: pipelines = json.load(sys.stdin)
except json.JSONDecodeError: sys.exit(1) # glab error already on stderr; don't add a traceback
active = [p for p in pipelines if p['status'] in ('running', 'pending', 'created')]
if len(active) <= 1:
print('Nothing to cancel — at most one active pipeline.'); sys.exit(0)
print(f'Keeping newest: #{active[0][\"id\"]} ({active[0][\"status\"]})')
for p in active[1:]:
print(f'Would cancel: #{p[\"id\"]} ({p[\"status\"]})')
" | cat
glab api commands into a loop — the IDs are filled in
automatically (nothing to copy-paste), and each cancel's resulting status is
printed, so a failure is visible rather than silent:if ! CANCEL_CMDS=$(glab api "projects/$PROJECT_ID/pipelines?ref=$BRANCH&sort=desc&per_page=10" | \
python3 -c "
import json, sys
try: pipelines = json.load(sys.stdin)
except json.JSONDecodeError: sys.exit(1) # glab error already on stderr; don't add a traceback
active = [p for p in pipelines if p['status'] in ('running', 'pending', 'created')]
for p in active[1:]:
print(f'glab api -X POST projects/$PROJECT_ID/pipelines/{p[\"id\"]}/cancel')
"); then
echo "Pipeline query failed — check PROJECT_ID and glab auth (see stderr above)."
elif [ -z "$CANCEL_CMDS" ]; then
echo "Nothing to cancel — at most one active pipeline."
else
echo "$CANCEL_CMDS" | while read -r cmd; do
echo "+ $cmd"
eval "$cmd" 2>&1 | python3 -c "import json,sys; print(' ->', json.load(sys.stdin).get('status','?'))" 2>/dev/null \
|| echo " -> FAILED (cancel did not return valid JSON)"
done
fi
$PROJECT_ID inside the Python f-string is expanded by the shell before
Python runs (the python3 -c body is in a double-quoted string), so each
emitted line carries the real numeric project id — it is not a missing Python
variable. eval "$cmd" is safe here: every emitted command is a fixed
glab api -X POST …/{id}/cancel string whose only interpolated value is an
integer pipeline id straight from the API — no user-controlled or
free-text fields are evaled.
(The listing calls don't redirect 2>&1 into Python: on an API error glab
writes to stderr and Python sees empty stdin. The try/except json.JSONDecodeError: sys.exit(1) then exits cleanly — no Python traceback — so glab's own stderr
message is the sole diagnostic, and the non-zero exit drives the if ! branch
to "Pipeline query failed". The eval-loop status parse is likewise wrapped
with 2>/dev/null so a non-JSON cancel response surfaces only as FAILED.)
The preview (step 2) is the confirmation gate — eyeball it before running step 3. For a per-command y/n prompt instead, replace the loop body with
read -p "run: $cmd ? " a </dev/tty && [ "$a" = y ] && eval "$cmd".
for BRANCH in branch1 branch2; do
echo "=== $BRANCH ==="
if ! CANCEL_CMDS=$(glab api "projects/$PROJECT_ID/pipelines?ref=$BRANCH&sort=desc&per_page=10" | \
python3 -c "
import json, sys
try: pipelines = json.load(sys.stdin)
except json.JSONDecodeError: sys.exit(1) # glab error already on stderr; don't add a traceback
active = [p for p in pipelines if p['status'] in ('running', 'pending', 'created')]
for p in active[1:]:
print(f'glab api -X POST projects/$PROJECT_ID/pipelines/{p[\"id\"]}/cancel')
"); then
echo " Pipeline query failed — check PROJECT_ID and glab auth (see stderr above)."
continue
elif [ -z "$CANCEL_CMDS" ]; then
echo " Nothing to cancel — at most one active pipeline."
else
echo "$CANCEL_CMDS" | while read -r cmd; do
echo "+ $cmd"
eval "$cmd" 2>&1 | python3 -c "import json,sys; print(' ->', json.load(sys.stdin).get('status','?'))" 2>/dev/null \
|| echo " -> FAILED (cancel did not return valid JSON)"
done
fi
done
To dry-run instead, echo "$CANCEL_CMDS" (or skip the if/else and just
print the variable) — that shows the raw glab api … cancel commands without
running them. For the friendly Would cancel: #N listing, run step 2's preview
per branch.
glab api "projects?search=<repo-name>" | python3 -c "...".success or failed pipelines — they're already done.claude-review job runs early and fast; the check-package job is usually
what's hogging the runner. Canceling a superseded pipeline frees the runner for
the newer one.glab api through | cat to avoid pager issues.