Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 109 additions & 21 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,129 @@ name: Release

on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
tags: ['v*']
workflow_dispatch:
inputs:
tag:
description: 'Tag to release (e.g. v0.5.0)'
required: true

# Prevent concurrent releases
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

permissions:
contents: write

jobs:
publish:
# ──────────────────────────────────────────────
# 1. CI gate — re-run full checks on tag code
# ──────────────────────────────────────────────
ci:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v7
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- uses: actions/setup-node@v6
- name: Setup Node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version-file: '.nvmrc'
registry-url: "https://registry.npmjs.org"
cache: 'npm'

# OIDC trusted publishing は npm >= 11.5.1、.npmrc の min-release-age は
# npm >= 11.10 が必要(未満だと未知 config 警告が stderr に出て
# E2E の「stderr 空」検証を壊す)。両方満たす版に固定する
- run: npm install -g npm@11.18.0
- name: Pin npm
run: npm install -g npm@11.18.0

- name: Install deps
run: npm ci --ignore-scripts

- run: npm ci --ignore-scripts
- name: Lint (Biome)
run: npx biome check cli/

- run: npx biome check cli/
- run: npx tsc --noEmit
- run: npx vitest run
- name: Typecheck
run: npx tsc --noEmit

- name: Verify tag matches package.json version
- name: Test
run: npx vitest run

# ──────────────────────────────────────────────
# 2. Publish to npm
# ──────────────────────────────────────────────
npm-publish:
needs: ci
runs-on: ubuntu-latest
environment: production
permissions:
id-token: write
contents: read
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- name: Setup Node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version-file: '.nvmrc'
cache: 'npm'
registry-url: 'https://registry.npmjs.org'

- name: Pin npm
run: npm install -g npm@11.18.0

- name: Install deps
run: npm ci --ignore-scripts

- name: Determine dist-tag
id: dist-tag
run: |
TAG="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}"
if echo "$TAG" | grep -qE '-(alpha|beta|rc)'; then
echo "tag=beta" >> "$GITHUB_OUTPUT"
else
echo "tag=latest" >> "$GITHUB_OUTPUT"
fi

- name: Set version from tag and sync publish artifacts
run: |
TAG="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}"
VERSION="${TAG#v}"
npm version "$VERSION" --no-git-tag-version
node scripts/sync-version.mjs
npx tsx scripts/gen-agents-catalog.ts

- name: Publish
run: npm publish --provenance --access public --tag ${{ steps.dist-tag.outputs.tag }}

# ──────────────────────────────────────────────
# 3. Create GitHub Release
# ──────────────────────────────────────────────
github-release:
needs: npm-publish
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0

- name: Determine release metadata
id: meta
run: |
PKG_VERSION=$(node -p "require('./package.json').version")
TAG_VERSION="${GITHUB_REF_NAME#v}"
if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then
echo "Tag $TAG_VERSION does not match package.json $PKG_VERSION" >&2
exit 1
TAG="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}"
if echo "$TAG" | grep -qE '-(alpha|beta|rc)'; then
echo "prerelease=true" >> "$GITHUB_OUTPUT"
else
echo "prerelease=false" >> "$GITHUB_OUTPUT"
fi

- run: npm publish --provenance --access public
- name: Create GitHub Release
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
with:
tag_name: ${{ github.ref_name || github.event.inputs.tag }}
generate_release_notes: true
prerelease: ${{ steps.meta.outputs.prerelease }}
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@

## [Unreleased]

### Changed

- リリースワークフローを `bitbank-lab-mcp` に揃えた。`npm version <bump>` の
ローカル前提を廃止し、tag push(`v*`)または `workflow_dispatch` で
`release.yml` が CI gate → version 注入・plugin manifest / agents カタログ同期
→ npm publish → GitHub Release 自動作成を実行する。OIDC trusted publisher の
environment は `production` に変更

### Added

- README: Cursor 節を拡充。導入 2 経路(リポジトリを開くだけ / Plugin として
Expand Down
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,6 @@ npx tsx cli/index.ts # CLI 実行
- コミット: `<type>: <概要>`(日本語 OK)
- 外部依存最小。`tsx` で直接実行。ビルドステップなし
- 開発フェーズ → [`docs/dev/phases.md`](docs/dev/phases.md)
- リリース手順 → [`docs/dev/release.md`](docs/dev/release.md)(`npm version <bump>` 経由で 5 ファイル同期。手動編集禁止)
- リリース手順 → [`docs/dev/release.md`](docs/dev/release.md)(tag push で release.yml が
version 注入・plugin manifest 同期・npm publish・GitHub Release を実行)
- Skill 追加 → `.claude/rules/skills.md`
5 changes: 3 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,6 @@ npx vitest run # テスト

## リリース(メンテナ向け)

npm publish とバージョン同期は `npm version <bump>` 経由で行います(5 ファイルを自動同期、
**手動編集禁止**)。手順は [docs/dev/release.md](docs/dev/release.md) を参照してください。
npm publish は tag push で `.github/workflows/release.yml` が起動し、version 注入・
plugin manifest 同期・agents カタログ再生成を経て publish する。手順は
[docs/dev/release.md](docs/dev/release.md) を参照してください。
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,9 @@ npm run typecheck # 型チェック

### リリース

npm publish 手順とバージョン同期の仕組みは [`docs/dev/release.md`](docs/dev/release.md) を参照。`npm version <bump>` で 5 ファイル一括同期されるので、`package.json` や plugin manifest を手動編集しないこと。
npm publish 手順は [`docs/dev/release.md`](docs/dev/release.md) を参照。`v*` tag を
push すると release workflow が version 注入・plugin manifest 同期・GitHub Release
作成まで実行する。

### コントリビューター向けセットアップ

Expand Down
2 changes: 1 addition & 1 deletion docs/dev/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ clone したユーザーは基本的に読む必要はない。

- `phases.md` — 開発フェーズの履歴と TODO
- `conventions.md` — メンテナ向けのコード規約メモ
- `release.md` — リリース手順(`npm version <bump>` で 5 ファイル同期
- `release.md` — リリース手順(tag push → release.yml で version 注入・publish
- `repo-security.md` — GitHub / npm の管理者向け初期セットアップ

配布側ユーザーが参照すべきドキュメントは `docs/customization-guide.md` /
Expand Down
95 changes: 68 additions & 27 deletions docs/dev/release.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,89 @@
# リリース手順

`bitbank-lab-cli` の npm publish フロー。
`bitbank-lab-cli` の npm publish フロー。`bitbank-lab-mcp` と同じ
tag 駆動 + GitHub Actions 多段階パイプライン。

## ワークフロー概要

`.github/workflows/release.yml` は 3 job で構成される:

1. `ci` — tag 上のコードで lint / typecheck / test を再実行
2. `npm-publish` — tag から version を注入し plugin manifest・agents カタログを
同期してから `npm publish --provenance`
3. `github-release` — GitHub Release を自動作成(リリースノート自動生成)

トリガー:

- `v*` tag の push(通常ルート)
- `workflow_dispatch`(手動起動、tag 名を入力)

## バージョン同期

`package.json` と plugin manifest 5 種(`.claude-plugin/plugin.json` /
`.cursor-plugin/plugin.json` / `.codex-plugin/plugin.json` /
`gemini-extension.json` / `plugin.json`)の 6 ファイルが同じ version を
持つ必要がある。**手動で個別編集しないこと**(同期漏れの温床)。
publish 時に release workflow が以下を実行する(ローカルでの事前同期は不要):

1. `npm version <tag-version> --no-git-tag-version` — `package.json` を tag に合わせる
2. `scripts/sync-version.mjs` — plugin manifest 5 種 + ルート `plugin.json` へ転写
3. `scripts/gen-agents-catalog.ts` — `agents/tool-catalog.json` /
`agents/error-catalog.json` を再生成(`cli_version` を埋め込む)

対象ファイル(計 6 + 生成物 2):

- `package.json`
- `.claude-plugin/plugin.json` / `.cursor-plugin/plugin.json` /
`.codex-plugin/plugin.json` / `gemini-extension.json` / `plugin.json`
- `agents/tool-catalog.json` / `agents/error-catalog.json`

ルートの `plugin.json` は Antigravity CLI(旧 Gemini CLI)のネイティブ
plugin manifest。旧 CLI 互換の `gemini-extension.json` と両置きすることで
新旧どちらの CLI からもリモート install できる。

`.claude-plugin/marketplace.json` も同じディレクトリにあるが、これは
plugin manifest ではなく marketplace カタログ。plugin の version 同期
対象外なので `scripts/sync-version.mjs` の targets には入れない
(オプションの `version` フィールドを将来追加する場合も同様)。
`.claude-plugin/marketplace.json` は marketplace カタログであり version 同期
対象外(`scripts/sync-version.mjs` の targets に入れない)。

`npm version <bump>` 実行時に `scripts.version` フック経由で
`scripts/sync-version.mjs` が走り、`package.json` の新 version を
plugin manifest に転写してから commit + tag が作られる。

あわせて同フックが `scripts/gen-agents-catalog.ts` を再実行し、
`agents/tool-catalog.json` / `agents/error-catalog.json`(どちらも
`cli_version` を埋め込む生成物)を新 version で再生成してステージする。
これらは手書きせず常に再生成する。再生成漏れは x17 ドリフトテストが
CI で検出する(`cli/__tests__/chaos/conventions/x17-agents-catalog-drift.test.ts`)。
main ブランチ上の `package.json` / plugin manifest は、公開済み npm version と
一致しないことがある(MCP と同様)。npm tarball 内では publish 直前に全て
揃う。

## 手順

```bash
npm version patch # 0.1.0 → 0.1.1 (6 ファイル同期 + commit + tag)
git push --follow-tags # tag を含めて push
# tag を push すると .github/workflows/release.yml が起動し、
# OIDC trusted publishing 経由で `npm publish --provenance` を実行する。
# 完了後に /tmp で動作確認 (鉄則)
cd /tmp && npx -y bitbank-lab-cli@<新 version> ticker btc_jpy
# 1. CHANGELOG の [Unreleased] を更新して main にマージ

# 2. tag を作成して push
git tag v0.2.1
git push origin v0.2.1

# tag push で release.yml が起動し、OIDC trusted publishing 経由で
# npm publish + GitHub Release が実行される。

# 3. 完了後に /tmp で動作確認 (鉄則)
cd /tmp && npx -y bitbank-lab-cli@0.2.1 ticker btc_jpy
```

### prerelease

`-alpha` / `-beta` / `-rc` を含む tag は npm dist-tag `beta` で公開され、
GitHub Release は prerelease として作成される:

```bash
git tag v0.3.0-beta.1
git push origin v0.3.0-beta.1
```

### 手動起動(workflow_dispatch)

GitHub Actions の Release workflow から手動実行し、tag 名(例: `v0.2.1`)を
指定できる。通常は tag push で十分。

### 手動 publish(フォールバック)

OIDC が使えない / workflow が失敗した場合の緊急用:

```bash
VERSION=0.2.1
npm version "$VERSION" --no-git-tag-version
node scripts/sync-version.mjs
npx tsx scripts/gen-agents-catalog.ts
npm publish --otp=<OTP>
```

Expand All @@ -55,14 +95,15 @@ provenance 表示が無くなる点に注意。
1. https://www.npmjs.com/package/bitbank-lab-cli/access で
"Trusted Publisher" を追加
2. GitHub repo: `bitbankinc/bitbank-lab-cli`、workflow: `release.yml`、
environment は空でよい
3. アカウント側で 2FA を `auth-and-writes` に設定(手動 publish 時の保険)
environment: `production`
3. GitHub repo に `production` environment を作成(Settings → Environments)
4. アカウント側で 2FA を `auth-and-writes` に設定(手動 publish 時の保険)

その他のリポジトリ側初回設定(branch protection / private vulnerability
reporting 等)は [`repo-security.md`](repo-security.md) を参照。

`patch` / `minor` / `major` は SemVer に従う。0.x は SemVer 上 minor で
breaking 可なので初期改修は `npm version patch` で増やしていく
breaking 可なので初期改修は patch 相当の tag を増やしていく

## publish 後の検証

Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@
"lint": "biome check cli/",
"lint:fix": "biome check --fix cli/",
"format": "biome format --write cli/",
"typecheck": "tsc --noEmit",
"version": "node scripts/sync-version.mjs && tsx scripts/gen-agents-catalog.ts && git add .claude-plugin/plugin.json .cursor-plugin/plugin.json .codex-plugin/plugin.json gemini-extension.json plugin.json agents/tool-catalog.json agents/error-catalog.json"
"typecheck": "tsc --noEmit"
},
"dependencies": {
"pubnub": "^12.0.0",
Expand Down
4 changes: 2 additions & 2 deletions scripts/sync-version.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env node
// Sync version from package.json to plugin manifests.
// Wired into `npm version` lifecycle via scripts.version in package.json,
// so `npm version patch` updates all 6 files in one commit.
// Invoked by .github/workflows/release.yml after `npm version --no-git-tag-version`
// so the published tarball carries matching plugin manifest versions.
// Regex (not JSON.parse → stringify) preserves each file's existing
// indentation and key order.
import { readFileSync, writeFileSync } from "node:fs";
Expand Down