Back to skills
SKILL.md
Release
ASecuritykaji の release 作業(version bump / CHANGELOG / tag / GitHub Release ページ)を進め、PyPI publish workflow へ引き継ぐ。
- 15 stars
- 0 votes
- 0 copies
- 0 views
- Added October 1, 2026
Works with
Security analysis
100/100npx -y skills add apokamo/kaji --skill release --agent claude-codeAre you the author of Release?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/apokamo-release)---
description: kaji の release 作業(version bump / CHANGELOG / tag / GitHub Release ページ)を進め、PyPI publish workflow へ引き継ぐ。
name: release
---
# Release
kaji の release を maintainer の手元で進める skill。
各 step で user 承認を挟みながら version bump → CHANGELOG → tag → GitHub Release ページ作成まで進める。
PyPI publish は GitHub Release 公開後に `.github/workflows/publish-pypi.yml` が
GitHub Actions + PyPI Trusted Publisher で実行する。
## いつ使うか
| タイミング | このスキル |
|-----------|-----------|
| kaji の release(version bump + tag + Release ページ)を切る | ✅ 必須 |
| dry-run(push / Release ページ作成手前まで確認したい) | ✅ `--dry-run` 経路 |
| PyPI publish | ✅ GitHub Release 公開後、`publish-pypi.yml` へ引き継ぐ |
| `.github/workflows/release-please.yml` の再有効化 | ❌ 対象外(本 skill は maintainer 手元実行前提) |
## 入力
```
/release # 通常実行(push と Release ページ作成まで進める)
/release --dry-run # ローカル状態のみ更新(push / gh release create はスキップ)
```
引数なしで起動し、skill 側で git / pyproject の状態を読んで判定する。
## 前提
- 実行場所: kaji 本体の **main worktree** で実行する(release branch は切らない、main 直接 update する運用)
- **`gh`** CLI を使う(GitHub 運用)
- GitHub を指す git remote が 1 つ存在すること。Step 1 で `git remote -v` の URL から動的に抽出する。`.kaji/config.toml` の `provider.github.git_remote` 値と整合する remote 名であることが前提
- 解決した GitHub remote に対して `git push` できる権限を maintainer が持っていること
- `uv` / `make check` が走る環境(kaji 開発環境セットアップ済み)
- PyPI publish を行う場合、GitHub environment `pypi` が作成済みで approval rule が設定されていること
- PyPI 側で project `kaji` の Trusted Publisher(初回は Pending Trusted Publisher)が設定済みであること
- owner: `apokamo`
- repository: `kaji`
- workflow filename: `publish-pypi.yml`
- environment: `pypi`
> **Note**: 現状 skill は `git remote -v` の URL grep でのみ remote を解決し、`.kaji/config.toml` の `provider.github.git_remote` は直接読まない。config 値を変えただけでは動作は変わらない点に注意。将来 `kaji config git-remote` 相当の CLI を追加する余地あり。
## 実行手順
### Step 1: Pre-flight check
以下を順に確認する。1 つでも失敗した時点で停止し、user に復旧手順を提示する。
```bash
# 1-0. GitHub remote 名を解決
# `git remote -v` から URL に github.com を含む push remote を動的抽出する。
# `.kaji/config.toml` の `provider.github.git_remote` 値と整合する
# remote 名であることが前提(skill は config を直接読まない)。
GITHUB_REMOTE=$(git remote -v | awk '/github\.com.*\(push\)/{print $1; exit}')
# 上記で見つからない場合、`origin` が GitHub を指していれば fallback
if [ -z "$GITHUB_REMOTE" ]; then
if git remote get-url origin 2>/dev/null | grep -q github; then
GITHUB_REMOTE=origin
else
echo "ABORT: no git remote pointing to github.com found"
exit 1
fi
fi
echo "Resolved GitHub remote: $GITHUB_REMOTE"
# 1-1. main checkout 状態
git rev-parse --abbrev-ref HEAD # → "main" であること
# 1-2. working tree clean
git status --porcelain # → 空であること
# 1-3. 上流 sync
git fetch "$GITHUB_REMOTE"
git rev-list --left-right --count "$GITHUB_REMOTE/main"...HEAD
# → "0\t0"(ahead/behind ともに 0)であること
# 1-4. 解決した remote が GitHub を指していることを再確認
git remote get-url "$GITHUB_REMOTE" # → github.* を含むこと
# 1-5. gh CLI 認証済み
gh auth status # → "Logged in" 表示
```
**失敗時のガイド例**:
- GitHub remote 未発見 → `.kaji/config.toml` の `provider.github.git_remote` および `git remote -v` 出力を user に提示し、追加方法を相談(`git remote add origin <github-url>`)
- main 以外 → `git checkout main && git pull --ff-only "$GITHUB_REMOTE" main`
- dirty tree → user に commit / stash を促す(skill 側では一切触らない)
- behind → `git pull --ff-only "$GITHUB_REMOTE" main`
- ahead → 未 push commit がある旨を user に伝え、release に含めるべきか確認
- remote 不一致 → 別 remote (`upstream` 等) を見るべきか user に確認
### Step 2: Next version の提案
```bash
# 直近 tag を取得
LAST_TAG=$(git describe --tags --abbrev=0)
# tag 以降の commit を取得(マージ commit 含む / メッセージ全文)
git log "${LAST_TAG}..HEAD" --pretty=format:'%H%n%B%n---END---'
```
取得した commit list を Conventional Commits で分類し、以下のルールで bump 種別を判定する。
| 検出 | bump |
|------|------|
| commit message body に `BREAKING CHANGE:` 行 / footer / `<type>!:` 形式 | major |
| `feat:` / `feat(...)` が 1 件以上 | minor |
| `fix:` / `fix(...)` / その他 (`docs:` / `chore:` / `refactor:` / `test:`) のみ | patch |
| commit が 0 件(tag 以降変更なし) | **ABORT** — release する変更が無い |
判定根拠(どの commit が major / minor の決定打になったか)を必ず user に提示する。
**user 承認待ち**: 提案 version (例: `v0.9.1 → v0.10.0`) と判定根拠を出力し、user の承認を待つ。
user が異なる version を希望する場合はそちらを採用する(初期運用では Conventional Commits 解釈ミスを警戒し、必ず承認を挟む)。
**release 必要性の追加確認**: 候補 commit が `docs:` / `test:` / `chore:` のみで `feat:` / `fix:` / BREAKING CHANGE を一切含まない場合、patch bump 候補となるが consumer 側 lockfile を無用に更新させるため SemVer 的には冗長になりがち。Step 2 の user 承認時に「今回の bump 候補は docs/test/chore のみ。本当に release するか」と明示的に問いかけ、user 判断で release skip / continue を決める。
### Step 3: CHANGELOG 生成
`CHANGELOG.md` の `## [Unreleased]` セクションを基に、新 version のエントリを作成する。
```markdown
## [X.Y.Z] - YYYY-MM-DD
### BREAKING CHANGE
- **壊れる契約**: 何を前提にしていた何が動かなくなるか
- **影響の判定方法**: 下流 repo が影響を受けるかを確認する手段(grep 等の 1 コマンドが理想)
- **適用指針**: 未カスタマイズなら再コピーで可の旨。カスタマイズ済み repo 向けには契約変更点の説明と上流 commit / PR への参照
### Added
- feat: ... entries
### Fixed
- fix: ... entries
### Changed / Docs / Internal
- その他 (docs / refactor / chore / test) entries
```
**BREAKING エントリの 3 要素(ADR 008 決定 2・必須)**: `### BREAKING CHANGE` に記載する
各項目は、**壊れる契約 / 影響の判定方法 / 適用指針** の 3 要素を必ず含める。kaji は
後方互換レイヤを提供しない(ADR 008)ため、破壊的変更の伝達は release notes 側の責務で
あり、この 3 要素が下流 repo の唯一の移行ガイドになる。3 要素が揃わない BREAKING
エントリのまま release を進めてはならない(参照:
[`docs/adr/008-no-backward-compat-layer.md`](../../../docs/adr/008-no-backward-compat-layer.md) /
[`docs/dev/shared_skill_rules.md`](../../../docs/dev/shared_skill_rules.md) § 後方互換(共通))。
進め方:
1. `## [Unreleased]` 配下の既存記述を新 version セクションへ移動
2. 不足分(Unreleased に未記載の commit)があれば commit message から要約を追記
3. `### BREAKING CHANGE` に項目がある場合、各項目が 3 要素(壊れる契約 / 影響の判定方法 / 適用指針)を満たすか確認し、欠けていれば補う
4. 末尾の比較リンク(存在する場合)を更新
5. `CHANGELOG.md` の **diff を user に提示** して承認待ち
**user 承認待ち**: user が文面修正を希望する場合は再 edit → 再提示。
### Step 4: Version bump
```bash
# 4-1. pyproject.toml の version を編集(Edit tool 経由で 1 箇所のみ書き換え)
# version = "X.Y.Z"
# 4-2. lockfile 同期
uv lock
# 4-3. 品質チェック
source .venv/bin/activate && make check
```
`make check` が失敗した場合は **Step 5 に進まず停止**。原因を user に提示し、修正方針を相談する。
### Step 5: Commit と tag
```bash
# 5-1. ステージング
git add pyproject.toml uv.lock CHANGELOG.md
# 5-2. release commit
git commit -m "chore(release): vX.Y.Z"
# 5-3. tag 付与(annotated tag を推奨)
git tag -a vX.Y.Z -m "Release vX.Y.Z"
```
`--dry-run` 経路の場合、ここまでで停止し、後述の **dry-run 終了処理** を案内する。
### Step 6: Push
Step 1 で解決した `$GITHUB_REMOTE` に対して、main と tag を **atomic push** する。
```bash
# main と tag を 1 トランザクションで push(どちらか失敗すれば両方拒否される)
git push --atomic "$GITHUB_REMOTE" main vX.Y.Z
```
`--atomic` を使うことで、main は push 成功 / tag push 失敗のような **片方だけ remote に反映された中途半端な状態** を防ぐ。
push 失敗時:
- `non-fast-forward` → main が他者 push で進んでいる。`--atomic` のため tag も remote には残らない。**force push は禁止**。一旦 stop し、user に状況を共有 → 必要なら Step 1 から再実行(commit と tag を一度ローカルで rollback、後述)
- tag 関連で reject(同名 tag が既に存在する等)→ atomic のため main も remote 反映されていない。`git tag -d vX.Y.Z` で local tag を消し、原因確認後に再試行
### Step 7: GitHub Release ページ作成
公開 notes には [starter sync runbook](../../../docs/operations/release/starter-sync-runbook.md) の
managed starters 表から repository 別状態表を作り、全行を `PENDING` で初期化する。各行は
独立に `PASS` または `N/A` へ遷移し、単一の集約 status は置かない。
```markdown
## Starter repositories
| repository | status | tracking Issue | starter Release / N/A 理由 |
|---|---|---|---|
| apokamo/kaji-starter-python | PENDING | - | - |
| apokamo/kaji-starter-typescript | PENDING | - | - |
```
```bash
# CHANGELOG の該当 section を抜粋し、--notes に渡す
gh release create vX.Y.Z \
--title "vX.Y.Z" \
--notes "<CHANGELOG.md の [X.Y.Z] section 本文>"
```
完了後、`gh release view vX.Y.Z` で URL を取得し user に提示する。
GitHub Release の `published` event により `.github/workflows/publish-pypi.yml` が起動する。
workflow は GitHub environment `pypi` の approval 後、Trusted Publisher で `uv publish` を実行する。
通常運用では PyPI API token を local 端末、`.pypirc`、GitHub Secrets に保存しない。
### Step 7.5: PyPI publish workflow 確認
`--dry-run` では本 step を実行しない。
```bash
gh run list --workflow publish-pypi.yml --limit 3
```
確認すること:
- workflow が対象 release / tag に対して起動している
- `pypi` environment approval 待ち、または approval 後に実行中である
- `Build distributions` / `Check package metadata` / `Smoke test wheel entry point` / `Publish to PyPI` が失敗していない
失敗時は PyPI project / Pending Trusted Publisher / GitHub environment `pypi` の設定値が
workflow と一致しているか確認する。特に workflow filename は `publish-pypi.yml` であること。
### Step 8: 完了報告
user に以下を提示して終了:
- 採番した version と tag URL
- GitHub Release ページ URL
- PyPI publish workflow の状態または URL
- PyPI 公開後の install 確認: `uv tool install kaji && kaji --help`
- consumer 側に `uv lock --upgrade-package kaji` を案内する一文(kamo2 等の dependency consumer 向け)
managed starter ごとに、公開した version を追随タスクとして tracking Issue へ反映する。
未完了期間ごとに 1 件へ集約するため、starter 側で新規 Issue を作る前に必ず
`kaji starter tracking-plan` の判定を経由する
([starter sync runbook](../../../docs/operations/release/starter-sync-runbook.md)):
```bash
# starter-sync label の open Issue 全件(他 starter 分も含む)を観測として渡す
kaji issue list --label starter-sync --state open --json number,body \
| jq '[.[] | {issue_id: .number, body: .body}]' > /tmp/open-tracking.json
kaji starter tracking-plan <<EOF
{
"starter_repo": "apokamo/kaji-starter-python",
"new_target": "vX.Y.Z",
"open_tracking_issues": $(cat /tmp/open-tracking.json),
"selected_issue_id": null
}
EOF
```
managed starter の checkout が
[Tracking Issue の表](../../../docs/operations/release/starter-sync-runbook.md#managed-starters)の
default local path と異なる場合は、`starter_path` にその絶対パスを渡す
(新規作成される tracking Issue 本文へそのまま反映される。省略時は本文に出力しない = 既定パス)。
`decision` ごとの対応:
| decision | 対応 |
|---|---|
| `CREATE` | `required_labels`(`starter-sync`)を付けて `next_body` で新規 Issue を作成し、Issue URL を GitHub Release の状態表 `tracking Issue` 列へ反映する |
| `APPEND` | 既存 Issue(`issue_id`)の本文を `next_body` へ更新する(新規 Issue は作らない)。状態表の `tracking Issue` 列は既存 Issue の URL のまま |
| `IDEMPOTENT` | 本文を変更しない(同一 version への release-plan 再実行や retry で発生し得る) |
| `ABORT` | 停止し `reason` を user に提示する。複数の未完了 Issue が見つかった場合は、どちらを使うか人間が `selected_issue_id` を指定してから再実行する。状態表は `PENDING` のまま維持し、kaji 本体の release は rollback しない |
tracking Issue の作成・更新後、`/update-starter <tracking_issue_id>` を案内する。
starter の追随・review・公開は kaji 本体 release とは独立したトランザクションとする。
starter が `PENDING` または失敗しても、公開済み kaji tag / GitHub Release / PyPI を rollback しない。
## Dry-run 経路(`--dry-run`)
Step 1 → 5 まで実行し、Step 6 (push)、Step 7 (Release ページ)、Step 7.5 (PyPI publish workflow 確認) を **スキップ**。
dry-run では tag push / GitHub Release 作成を行わないため、PyPI publish workflow も起動しない。
dry-run 終了時に skill が必ず提示する内容:
1. **作成された commit と tag**: `git show vX.Y.Z --stat` の要点
2. **rollback 手順**(dry-run のみ。本番経路では使わない):
> ⚠️ 以下は `git reset --hard` を含む破壊操作。skill が自動実行してはならない(ガードレール § も参照)。
> **必ず user の明示承認を取ってから実行する**。コピペで一度に流す前提のブロックではない。
```bash
git tag -d vX.Y.Z
git reset --hard HEAD~1 # release commit を破棄(破壊操作: user 承認後のみ)
# CHANGELOG.md / pyproject.toml / uv.lock の変更を確認後、必要なら git restore で戻す
```
3. **本番実行への進み方**:
- dry-run の結果に問題がなければ `git push --atomic "$GITHUB_REMOTE" main vX.Y.Z` を手で実行 → Step 7 を手で実行、
- または rollback してから `/release`(dry-run なし)で再実行
## 失敗時の rollback 手順(共通)
各 step ごとに、失敗 → 復旧の流れを以下に集約する。
### Step 5 (commit/tag) の途中で失敗
```bash
# tag を作る前に commit が失敗した場合
git restore --staged pyproject.toml uv.lock CHANGELOG.md
git restore pyproject.toml uv.lock CHANGELOG.md
# commit はできたが tag 付与で失敗した場合
git tag -d vX.Y.Z # 失敗していれば存在しない
git reset --hard HEAD~1
```
### Step 6 (push) で remote 拒否
`git push --atomic` を使うため、reject 時は **main と tag のどちらも remote には反映されていない**。ローカル side だけ巻き戻せばよい。
```bash
# 状況確認
git fetch "$GITHUB_REMOTE"
git log --oneline "$GITHUB_REMOTE/main"..HEAD # 自分のローカル commits を確認
git log --oneline HEAD.."$GITHUB_REMOTE/main" # 他者の commits を確認
# 他者 commits を取り込む必要がある場合(force push 禁止):
# 1. tag を一旦削除
git tag -d vX.Y.Z
# 2. release commit を一旦巻き戻す
git reset --hard "$GITHUB_REMOTE/main"
# 3. main を最新化してから /release を再実行
git pull --ff-only "$GITHUB_REMOTE" main
```
> **絶対禁止**: `git push --force "$GITHUB_REMOTE" main` / `git push --force "$GITHUB_REMOTE" vX.Y.Z`。
> tag を上書き push (`--force`) すると consumer 側の lockfile / cache 整合が壊れる。tag は不変前提で運用する。
### Step 6 まで成功し Step 7 (Release ページ) で失敗
tag と main commit は既に push 済み。**rollback ではなく Release ページのみ再試行**する:
```bash
gh release view vX.Y.Z 2>/dev/null || \
gh release create vX.Y.Z --title "vX.Y.Z" --notes "<本文>"
```
それでも作成できない場合、GitHub UI から手動で Release ページを作る選択肢を user に提示する。
### Step 7.5 で PyPI publish workflow が失敗
main commit / tag / GitHub Release は作成済みなので、release commit を rollback しない。
以下を確認してから、失敗した workflow を GitHub UI で rerun する:
- PyPI Pending Trusted Publisher または project Trusted Publisher の workflow filename が `publish-pypi.yml`
- PyPI Trusted Publisher の owner / repository / environment が `apokamo` / `kaji` / `pypi`
- GitHub environment `pypi` が存在し、approval rule が意図どおり
- workflow job に `permissions.id-token: write` がある
- `uvx twine check --strict dist/*` の失敗なら README / metadata を修正し、次の patch release で再 publish する
PyPI API token による local `uv publish` は emergency fallback のみ。使用する場合も token を
`.pypirc`、shell history、Issue コメント、docs、repo 内ファイルに残さない。
### Starter post-release handoff が失敗
kaji 本体 tag / GitHub Release / PyPI は公開済みのため rollback しない。GitHub Release の対象行を
`PENDING` のまま維持し、`kaji starter tracking-plan` を同じ観測で再実行する(`CREATE` / `APPEND` は
入力から決定的に再計算されるため再試行は冪等)。状態表リンク更新だけが失敗した場合も、その反映
だけを再試行する。starter の具体的な復旧は
[starter sync runbook](../../../docs/operations/release/starter-sync-runbook.md) に従う。
### 既に push 済みの release を撤回したい(緊急)
原則として撤回しない。撤回する場合は別 issue で意思決定を残し、以下を user の明示同意付きで行う:
```bash
# tag をリモートから削除
git push "$GITHUB_REMOTE" --delete vX.Y.Z
# Release ページを削除
gh release delete vX.Y.Z
# 必要なら revert commit を main に乗せる(force push はしない)
git revert <release-commit-sha>
git push "$GITHUB_REMOTE" main
```
## ガードレール
- main 以外の branch では起動しない(Step 1 で reject)
- working tree が dirty な状態で commit を作らない
- `git push --force` / tag の force push を skill 側からは絶対に実行しない
- `make check` を pass しないまま Step 5 以降に進まない
- user 承認ポイント(Step 2 version / Step 3 CHANGELOG)を勝手にスキップしない
- 失敗時は停止して user にガイドする(自動 retry / 自動 rollback はしない、commit/tag の rollback は user 承認後に skill が実行可)
## 出力(verdict)
```text
---VERDICT---
status: PASS | ABORT
reason: |
release を完了した(または dry-run を完了した)
evidence: |
- 採番 version: vX.Y.Z
- commit sha: ...
- tag URL / Release ページ URL(本番経路のみ)
- dry-run の場合: 作成済み commit/tag と rollback 手順を提示済み
suggestion: |
consumer 側で `uv lock --upgrade-package kaji` を実行するよう案内(本番経路のみ)
---END_VERDICT---
```
`ABORT` を返すケース:
- Step 1 の pre-flight check が修復困難(remote 不一致 / 認証不能 等)
- Step 2 で release 対象 commit が 0 件
- Step 4 で `make check` が失敗し、user が修正を保留した
- Step 6 で push が継続的に拒否され、原因究明が release skill の責務を超える
Attribution
Comments
Loading comments…