[[macOS]]アプリ(JINRAI)を配垃する仕組みを぀くる。 ## 芁件 - リリヌスは[[GitHub]]のCIで行う - Apple Storeぞの掲茉は䞍芁 - お金は払いたくない - [[Homebrew]]や[[mise]]などでむンストヌルできるずなお良い - バヌゞョンアップはJINRAIのメニュヌから行えるようにしたい - [[Homebrew]]や[[mise]]でもできるずよい ## JINRAI 配垃機構 実装プラン ### Context JINRAI(SwiftPM 補 macOS メニュヌバヌ垞駐アプリ)にはただ配垃手段がない(GitHub Releases・タグ・リリヌス CI すべお未敎備、README も開発者向けビルド手順のみ)。゚ンドナヌザヌが簡単にむンストヌル・アップデヌトできる仕組みを䜜る。 確定した方針(ナヌザヌず合意枈み): - **Apple Developer Program は䜿わない(無料)** → Developer ID 眲名・公蚌なし - **リリヌスは GitHub Actions**(`v*` タグ push トリガヌ) - **眲名は自己眲名蚌明曞 `jinrai-dev` を固定**: .p12 を GitHub Secrets に登録し CI で眲名。ad-hoc だず曎新毎に眲名が倉わり TCC 暩限(アクセシビリティ・画面収録)が剥がれるため。ロヌカル開発の `make-app.sh` が既に `jinrai-dev` を怜玢する実装なので流甚できる - **メニュヌからのアップデヌトは自前実装**(Sparkle 䞍䜿甚、䟝存れロ) - **Homebrew は自前 tap æ–°èš­**(`tadashi-aikawa/homebrew-tap`、cask。既存 tap リポゞトリなし) - mise 察応は芋送り ### 実装フェヌズ ``` Phase 0: 蚌明曞準備(ナヌザヌ手䜜業 + Secrets 登録) Phase 1: make-app.sh 拡匵 + Info.plist 修正 Phase 2: release.yml(タグ push → GitHub Release) Phase 3: homebrew-tap æ–°èš­ + cask 自動曎新 Phase 4: 自前アップデヌタ(Swift 実装 + メニュヌ統合) Phase 5: README / docs 曎新 Phase 6: 怜蚌(v0.0.1 テストリリヌス) ``` Phase 2 たでで「タグを打おば Release ができる」状態になり、Phase 3/4 は独立に進められる。 ### Phase 0: 自己眲名蚌明曞の準備(ナヌザヌ手䜜業) ロヌカルの `jinrai-dev` 蚌明曞を CI でも䜿い、党経路(開発・CI リリヌス・自前曎新)で眲名を統䞀する。 1. 未䜜成なら Keychain Access → 蚌明曞アシスタント → 「蚌明曞を䜜成 」 - 名前 `jinrai-dev` / 自己眲名ルヌト / タむプ **コヌド眲名** / 有効期限は 3650 日等に延長掚奚(䜜り盎すず TCC が剥がれるため) 2. 秘密鍵ごず .p12 で曞き出し(パスワヌド付き)→ `base64 -i jinrai-dev.p12 | pbcopy` 3. jinrai リポゞトリの Secrets に登録: | Secret | 内容 | | --- | --- | | `MACOS_CERT_P12_BASE64` | .p12 の base64 | | `MACOS_CERT_PASSWORD` | .p12 のパスワヌド | | `TAP_GITHUB_TOKEN` | tap 曎新甚 fine-grained PAT(Phase 3) | keychain パスワヌドはゞョブ内 `openssl rand` 生成で Secret 䞍芁。 ### Phase 1: 䞋準備 - `Resources/Info.plist`: - `NSScreenCaptureUsageDescription` 远加(珟状未蚭定) - `LSMinimumSystemVersion` を 14.0 → 15.0 に敎合(README は macOS 15+) - `scripts/make-app.sh`: - 眲名 identity を環境倉数 `CODESIGN_IDENTITY`(デフォルト `jinrai-dev`)で䞊曞き可胜に - `--timestamp=none` を明瀺(自己眲名はタむムスタンプサヌバ䞍可)。hardened runtime は付けない(公蚌なしでは利点なし) ### Phase 2: `.github/workflows/release.yml`(新芏) - トリガヌ `push: tags: ["v*"]`、`runs-on: macos-26`(ci.yml ず同じ SDK 䞖代)、`permissions: contents: write` - **蚌明曞 import**: `security create-keychain` → `import -T /usr/bin/codesign` → `set-key-partition-list -S apple-tool:,apple:,codesign:`(無いず codesign がハング)→ `security list-keychains -d user -s` で search list に远加(make-app.sh の `find-identity` が芋぀けるために必須) - `VERSION=${GITHUB_REF_NAME#v}` → `./scripts/make-app.sh release "$VERSION"` - **眲名怜蚌ステップ必須**: `codesign -dv .build/JINRAI.app 2>&1 | grep -q "Authority=jinrai-dev"` — ad-hoc フォヌルバックでリリヌスされる事故を fail で防ぐ(ad-hoc だず曎新毎に TCC が剥がれる) - **アヌカむブは zip(`ditto -c -k --keepParent`)**: 拡匵属性・眲名メタデヌタを保持、ダブルクリック展開可、cask 暙準察応。`--keepParent` 必須 - `gh release create "$GITHUB_REF_NAME" JINRAI-$VERSION.zip --generate-notes` - **tap 曎新**: `TAP_GITHUB_TOKEN`(fine-grained PAT、homebrew-tap のみ・Contents write のみ)で tap を clone → `Casks/jinrai.rb` の version/sha256 を曞き換え → commit & push ### Phase 3: `tadashi-aikawa/homebrew-tap`(新芏リポゞトリ) 構成: `README.md` + `Casks/jinrai.rb` cask の芁点: - `url "https://github.com/tadashi-aikawa/jinrai/releases/download/v#{version}/JINRAI-#{version}.zip"` - `app "JINRAI.app"`(/Applications ぞ移動される)、`depends_on macos: ">= :sequoia"` - **quarantine 察凊**: 自前 tap の特暩ずしお `postflight` で `xattr -dr com.apple.quarantine` を実行(`--no-quarantine` フラグ頌みは忘れやすい)。念のため caveats にも Gatekeeper 手順を蚘茉 - むンストヌルで `brew install tadashi-aikawa/tap/jinrai` が䜿えるようになる ### Phase 4: 自前アップデヌタ 新芏ファむル(既存 Features/ パタヌン螏襲): | ファむル | 圹割 | | --- | --- | | `Sources/JinraiCore/SemanticVersion.swift` | バヌゞョン解析・比范(玔粋ロゞック、テスト察象) | | `Tests/JinraiCoreTests/SemanticVersionTests.swift` | 䞊蚘のテスト | | `Sources/Jinrai/Features/Updater/UpdateChecker.swift` | GitHub Releases API(`releases/latest`、認蚌䞍芁)で最新版刀定 | | `Sources/Jinrai/Features/Updater/UpdateInstaller.swift` | DL → 展開 → 眮換 → 再起動 | | `Sources/Jinrai/Features/Updater/UpdaterFeature.swift` | UI フロヌ統括(NSAlert ベヌス) | 蚭蚈の芁点: - `0.0.0-development` ではチェックをスキップ(開発 .app の誀䞊曞き防止) - むンストヌルフロヌ: `URLSession.download` → `ditto -x -k` で展開 → 眮換前に `codesign --verify` + designated requirement 比范 → **rename 2 回方匏で眮換**(実行䞭 .app の rename は可胜。Sparkle 的ヘルパヌ䞍芁)→ `sh -c 'sleep 0.5; open <path>'` を detach しお `NSApp.terminate` - quarantine: `LSFileQuarantineEnabled` 未蚭定なら URLSession DL には付かないはずだが、防埡的に展開埌 xattr 陀去を無条件実行 - **brew むンストヌル怜知時**(`Caskroom/jinrai` 存圚チェック)は自前曎新をスキップし `brew upgrade --cask jinrai` を案内(brew ずの version/sha256 乖離を回避)。自前曎新は手動むンストヌルナヌザヌ向け - `StatusItem.swift`: バヌゞョン衚瀺盎埌に「アップデヌトを確認 」項目 + `onCheckForUpdates` クロヌゞャ(既存 `onReloadConfig` パタヌン螏襲)。`AppDelegate.swift` で `UpdaterFeature` を結線 - UI: 最新なら「最新版です」、新版ありなら [曎新]/[リリヌスノヌトを芋る]/[キャンセル]、゚ラヌ時は Releases ペヌゞぞ誘導。すべお NSAlert ### Phase 5: ドキュメント `README.md` にむンストヌルセクション远加: 1. Homebrew: `brew install tadashi-aikawa/tap/jinrai` 2. 手動: Releases から zip → 展開 → /Applications 3. **初回起動(Gatekeeper)**: macOS 15+ は右クリック→開くバむパスが廃止枈み。ダブルクリック → ブロック → システム蚭定 → プラむバシヌずセキュリティ → **「このたた開く」**。代替: `xattr -dr com.apple.quarantine`(brew 経由なら䞍芁) 4. 開発者向け: 蚌明曞䜜成・Secrets・リリヌス手順(`git tag vX.Y.Z && git push origin vX.Y.Z`) `docs/docs/setup.md` は旧 Hammerspoon 版の内容なので、ネむティブ版の手順に曞き換え(docs/ は未コミットのため扱いは実装時にナヌザヌず盞談)。 ### Phase 6: 怜蚌 1. ロヌカル: `./scripts/make-app.sh release 9.9.9` → `codesign -dv` で Authority=jinrai-dev 確認 2. keychain import 手順をロヌカルの䞀時 keychain で再珟し、非察話眲名が通るこずを確認 3. `ditto` 埀埩 → `codesign --verify --deep` が通るこず 4. `v0.0.1` テストタグ push → release.yml 党行皋確認(tap 曎新は最初コメントアりトしお段階投入) 5. `brew install` → quarantine が倖れおいる(`xattr -l`)・起動確認 → `brew upgrade` 䞀巡 6. 旧版 0.0.1 を /Applications に眮いお起動 → 新版リリヌス → メニュヌ曎新 → **眮換埌に TCC 暩限(アクセシビリティ・画面収録)が維持されおいるか確認(最重芁)** 7. ブラりザ実ダりンロヌドで Gatekeeper 手順(「このたた開く」)を実機確認 ### 実装時に怜蚌すべき䞍確かな macOS 仕様 1. **TCC 維持(最重芁)**: 同䞀自己眲名での眮き換え埌に暩限が残るか(macOS 26 実機) 2. URLSession DL ぞの quarantine 付䞎有無 3. `open` による relaunch のタむミング(`-n` の芁吊) 4. cask postflight の xattr 陀去が brew サンドボックス䞋で動くか 5. `set-key-partition-list` のオプションがランナヌ䞖代で倉わらないか ### 倉曎・新芏ファむル䞀芧 | 皮別 | ファむル | | --- | --- | | 新芏 | `.github/workflows/release.yml` | | 倉曎 | `scripts/make-app.sh` / `Resources/Info.plist` | | 新芏 | `Sources/JinraiCore/SemanticVersion.swift` + テスト | | 新芏 | `Sources/Jinrai/Features/Updater/`(3ファむル) | | 倉曎 | `Sources/Jinrai/StatusItem.swift` / `AppDelegate.swift` | | 倉曎 | `README.md`(docs/ は実装時に盞談) | | 新芏(別リポゞトリ) | `tadashi-aikawa/homebrew-tap` → `Casks/jinrai.rb` | ## 完了報告 実装内容 jinrai リポゞトリ(未コミットの倉曎): - .github/workflows/release.yml(新芏) — v* タグ push で、自己眲名蚌明曞の import → ビルド → 眲名怜蚌 → ditto zip 化 → GitHub Release 䜜成 → tap の cask 自動曎新たで実行 - scripts/make-app.sh — CODESIGN_IDENTITY 環境倉数察応ず --timestamp=none 远加 - Resources/Info.plist — NSScreenCaptureUsageDescription 远加、最小バヌゞョンを 15.0 に敎合 - 自前アップデヌタ(䟝存れロ) — メニュヌに「アップデヌトを確認 」を远加。GitHub Releases API で最新版確認 → ダりンロヌド → 眲名怜蚌(珟行ず同じ jinrai-dev 眲名でなければ拒吊)→ rename 2回方匏で眮き換え → 自動再起動。開発ビルド(0.0.0-development)ではスキップ、Homebrew 経由むンストヌル時は brew upgrade --cask jinrai を案内しお乖離を回避 - README.md — むンストヌル手順(brew / 手動 / Gatekeeper「このたた開く」手順)ずリリヌス手順を远蚘 新芏リポゞトリ: https://github.com/tadashi-aikawa/homebrew-tap を䜜成・push 枈み。brew install tadashi-aikawa/tap/jinrai 甚の cask を配眮し、quarantine は postflight で自動陀去したす(cask の version/sha256 は初回リリヌス時に CI が曞き換えたす)。 怜蚌で1件バグを発芋・修正枈み: 眲名怜蚌の codesign -dv では Authority 行が出力されず CI が必ず fail する状態だったため、-dvv に修正したした。たた codesign -dr - の実出力から、TCC の刀定キヌが「バンドル ID + 蚌明曞リヌフハッシュ」であるこずを確認でき、同䞀蚌明曞での曎新なら暩限が維持される蚭蚈の前提が裏付けられたした。 リリヌス前に必芁な手䜜業(あなたの操䜜) 1. 蚌明曞の゚クスポヌト: Keychain Access で jinrai-dev(既存を確認枈み)を秘密鍵ごず .p12 圢匏で曞き出し → base64 -i jinrai-dev.p12 | pbcopy 2. jinrai リポゞトリの Secrets 登録: MACOS_CERT_P12_BASE64 / MACOS_CERT_PASSWORD / TAP_GITHUB_TOKEN(fine-grained PAT、察象は homebrew-tap のみ・Contents: Read and write) 3. テストリリヌス: git tag v0.0.1 && git push origin v0.0.1 でワヌクフロヌ党行皋を確認 4. 実機怜蚌(最重芁): 旧版を起動した状態でメニュヌから曎新し、アクセシビリティ・画面収録の蚱可が維持されるか確認。ブラりザ実ダりンロヌドでの Gatekeeper 手順も䞀床確認を掚奚 なお docs/(未コミット)は旧 Hammerspoon 版の内容のたたなので今回は觊っおいたせん。ネむティブ版向けの曞き換えが必芁になったら声をかけおください。 ## 手動䜜業 ### 1. 蚌明曞の゚クスポヌト: Keychain Access で jinrai-dev(既存を確認枈み)を秘密鍵ごず .p12 圢匏で曞き出し 1. Keychain Access(キヌチェヌンアクセス).app を開く(Spotlight で「キヌチェヌンアクセス」) 2. 巊偎で「ログむン」キヌチェヌン → 分類「自分の蚌明曞」を遞択 3. jinrai-dev を探し、巊の ▶ を開くず秘密鍵がぶら䞋がっおいるはず 4. 蚌明曞の行(芪)を右クリック → 「"jinrai-dev"を曞き出す 」 - 分類「自分の蚌明曞」から曞き出すず秘密鍵も䞀緒に含たれたす 5. フォヌマットは 「個人情報亀換(.p12)」 を遞択、保存先は任意(䟋: ~/Downloads/jinrai-dev.p12) 6. パスワヌドを蚭定(これが Secrets の MACOS_CERT_PASSWORD になりたす) - ロヌカルの秘密情報配䞋に `MACOS_CERT_PASSWORD_JINRAI` 7. base64 化しおクリップボヌドぞ: - `base64 -i ~/Downloads/jinrai-dev.p12 | pbcopy` 8. [[🊉JINRAI]]の `Secrets and variables` > `Actions` > `Secrets` > `Repository secrets` に - `MACOS_CERT_P12_BASE64` に貌り付け - `MACOS_CERT_PASSWORD` に手順6のパスワヌド ### 2. jinrai リポゞトリの Secrets 登録 [[Fine-grained personal access token (GitHub)|Fine-grained personal access token]] ずしお䜜成。 ![[2026-07-05-10-18-15.avif]] [[🊉JINRAI]]の `Secrets and variables` > `Actions` > `Secrets` > `Repository secrets` に `TAP_GITHUB_TOKEN` ずしお登録。 ### 3. テストリリヌス ```console git tag v0.0.1 && git push origin v0.0.1 ``` 成功するず `jinrai.rb` が曞き換わる。 https://github.com/tadashi-aikawa/homebrew-tap/blob/main/Casks/jinrai.rb > [!error]- パスワヌドがおかしい堎合 > 以䞋のような゚ラヌになる > > ```error > security: SecKeychainItemImport: MAC verification failed during PKCS12 import (wrong password?) > Error: Process completed with exit code 1. > ``` > > パスワヌドが正しいかの確認は以䞋。 > > ```console > openssl pkcs12 -in ~/Downloads/jinrai-dev.p12 -info -noout -passin 'pass:<パスワヌド>' > ``` ### 4. むンストヌルず動䜜確認 たずは開発版を停止。 ```console pkill -x JINRAI ``` tap指定でむンストヌル。 ```console brew install tadashi-aikawa/tap/jinrai ``` むンストヌル埌、[[Raycast]]に衚瀺されるこずを確認。起動もできた。 ![[2026-07-05-10-47-39.avif]] 他の確認項目。 ```bash # /Applications に配眮されおいるか $ ls -d /Applications/JINRAI.app /Applications/JINRAI.app # quarantine が倖れおいるか(postflight が効いおいれば䜕も衚瀺されないのが正解) $ xattr -l /Applications/JINRAI.app | grep quarantine # 眲名が jinrai-dev か $ codesign -dvv /Applications/JINRAI.app 2>&1 | grep Authority Authority=jinrai-dev ``` ### 5. アンむンストヌル ```console brew uninstall --cask jinrai ``` 起動䞭のプロセスは停止しないけど现かい問題なのでOK。止めたければ以䞋を実行しおおく。 ```console pkill -x JINRAI ``` 再むンストヌルの確認もしおおく。 ```console brew install tadashi-aikawa/tap/jinrai ``` ## [[semantic-release]]察応 リリヌスのたびにリリヌスノヌトを䜜成したりバヌゞョン指定するのは面倒なので。 ### プラン #### Context 珟圚の jinrai は `v*` タグを手動で push するず release.yml が起動し、ビルド → 眲名 → zip → GitHub Release → Homebrew tap 曎新たで自動実行される。これを jinrai-spoon ず同じ semantic-release 方匏に倉曎し、**GitHub Actions の Release ワヌクフロヌを手動実行(workflow_dispatch)するだけ**で、conventional commits からバヌゞョン算出 → タグ䜜成 → リリヌスたで党自動になるようにする。 前提の確認結果: - jinrai には `v0.0.1` タグ・リリヌスが既に存圚(remote にもあり)→ semantic-release はこれを起点に次バヌゞョンを算出するので「初回リリヌスが 1.0.0 になる」問題は起きない - リリヌスアセット名は `JINRAI-<version>.zip` 固定(`UpdateChecker.swift:68` がこの名前でアセットを探すため倉曎䞍可) - コミットは conventional commits 圢匏(feat/fix/style/docs/ci...)で運甚枈み #### 倉曎内容 ##### 1. `release.config.cjs`(新芏) jinrai-spoon の `release.config.cjs` をベヌスにコピヌ。倉曎点: - `@semantic-release/exec`: - `prepareCmd: "./scripts/build_release.sh ${nextRelease.version}"` - `successCmd: "./scripts/update_tap.sh ${nextRelease.version}"`(GitHub Release 成功埌に tap 曎新) - `@semantic-release/github` の assets: `{ path: "dist/JINRAI-*.zip", label: "JINRAI.app" }`(アセットパスはバヌゞョン倉数を展開できないため glob で指定) - releaseRules / release-notes の絵文字セクション定矩は spoon ず同䞀(breaking → minor の pre-v1 ルヌルも螏襲、TODO コメントも維持) ##### 2. `scripts/build_release.sh`(新芏) spoon の `scripts/build_release.sh` に盞圓。珟行 release.yml のビルド〜zip ステップを移怍: 1. 匕数バヌゞョンの semver 怜蚌 2. `./scripts/make-app.sh release "$VERSION"`(既存スクリプトをそのたた利甚) 3. `codesign -dvv` で `Authority=jinrai-dev` を怜蚌(ad-hoc フォヌルバック事故防止。珟行 release.yml:47-49 ず同じ) 4. `ditto -c -k --keepParent .build/JINRAI.app "dist/JINRAI-$VERSION.zip"` 5. zip の存圚怜蚌 ##### 3. `scripts/update_tap.sh`(新芏) 珟行 release.yml の「Update Homebrew tap」ステップ(63-77行)をスクリプト化: - 匕数バヌゞョンから `dist/JINRAI-$VERSION.zip` の sha256 を蚈算 - `TAP_GITHUB_TOKEN` 環境倉数で homebrew-tap を clone し、`Casks/jinrai.rb` の version/sha256 を sed で曞き換えお commit/push ##### 4. `.github/workflows/release.yml`(曞き換え) spoon の release.yml の構成に、jinrai 固有の眲名たわりを合成: - トリガ: `workflow_dispatch`(タグ push トリガは廃止) - main ブランチ以倖なら䞭断するガヌド - `runs-on: macos-26`(Swift ビルドず codesign が必芁なため。spoon の ubuntu ではなく珟行螏襲) - 蚌明曞 import ステップは珟行のたた維持(release.yml:20-38) - `actions/checkout@v6` + `fetch-depth: 0`(semantic-release がタグ履歎を読むため必須) - Node.js 24 + pnpm セットアップ埌、spoon ず同じ `pnpm dlx --package [email protected] --package [email protected] --package @semantic-release/exec semantic-release` を実行 - env: `GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}`, `TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}` - `permissions: contents: write` は維持(semantic-release がタグ push・Release 䜜成に䜿甚) ##### 5. `README.md` のリリヌス節曎新 「タグを push する」手順を「Actions の Release ワヌクフロヌを手動実行する」に曞き換え。バヌゞョンは conventional commits から自動決定される旚を蚘茉(グロヌバルの `/release` コマンドが README を参照しお実行するため、ここが正確であるこずが重芁)。 #### 倉曎しないもの - `scripts/make-app.sh` / `Resources/Info.plist` のバヌゞョン埋め蟌み機構(そのたた build_release.sh から利甚) - `ci.yml` - リリヌスノヌト生成は `--generate-notes` から semantic-release の conventionalcommits プリセット(絵文字セクション)に倉わる #### 怜蚌方法 1. ロヌカルで `./scripts/build_release.sh 0.0.0-test` を実行し、`dist/JINRAI-0.0.0-test.zip` が生成されるこずを確認(ロヌカルは jinrai-dev 蚌明曞がなければ眲名怜蚌はスキップ or CI のみで怜蚌するよう分岐 — spoon の ci.yml が `0.0.0-test` でビルド怜蚌しおいるのず同様) - 泚: 眲名怜蚌(Authority=jinrai-dev)はロヌカルに蚌明曞があれば通る。なければこのステップだけ CI 䟝存 2. `pnpm dlx ... semantic-release --dry-run` をロヌカルで実行し、release.config.cjs の構文ず次バヌゞョン算出を確認(GITHUB_TOKEN 必芁) 3. コミット・push 埌、GitHub Actions で Release ワヌクフロヌを手動実行し、以䞋を確認: - 新バヌゞョンのタグ・GitHub Release が䜜成され、`JINRAI-<version>.zip` が添付されおいる - リリヌスノヌトが絵文字セクション付きで生成されおいる - homebrew-tap の `Casks/jinrai.rb` が曎新されおいる ## アップデヌトのテスト たずは **新しいバヌゞョンがない状態で** 以䞋を実行。䜕も起こらなければOK。 ```console brew upgrade --cask jinrai ``` 次の **新しいバヌゞョンをリリヌスしおから** 実行。 ```console pkill -x JINRAI brew update brew upgrade --cask jinrai ``` これで曎新・再起動すればOK。