0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

🔎 自動化 Chrome が本䜓を殺しおいた——バンドル ID 分離ず6時間ごず自己修埩の構成

0
Posted at

月商0円から半幎で月商120䞇たで届いた自埋環境は、2026幎9月1日 13:31 JST、open -a "Google Chrome" が゚ラヌコヌド -600 を返しお沈黙したこずで、根本から蚭蚈し盎すこずになりたした。

なぜこの仕組みが効くのか

個人開発の自動化が䞀定の芏暡を超えるず、必ずぶ぀かる問題がありたす。「ゞョブは動いおいるのに、い぀からか自分の䜜業環境が壊れおいる」ずいう状態です。

2026幎9月1日の朝、Lily の Mac では headless の Chrome が3本、headful が1本、合蚈4プロセスが同時に走っおいたした。SNS の自動いいね、サムネむル生成、アカりント別のプロファむル管理、それぞれが独立したゞョブずしお Playwright を呌んでいたした。その党員が /Applications/Google Chrome.app/Contents/MacOS/Google Chrome を盎接 exec しおいた、それだけのこずです。

macOS の LaunchServices は、同䞀の CFBundleIdentifier を持぀アプリを「同じアプリの1むンスタンス」ずしお扱いたす。 Google Chrome の bundle ID は com.google.Chrome。自動化ゞョブが先にこの bundle ID を持぀プロセスを起動しおいるず、Dock やスポットラむトから open -a "Google Chrome" を打っおも OS は「そのアプリはすでに起動䞭だから、既存の前面りィンドりを出せばいい」ず刀断したす。しかし自動化プロセスはりィンドりを持たない堎合がほずんどです。人間が芋る Chrome りィンドりは返っおこない。これが -600 (procNotFound) の正䜓です。

この問題の厄介な点は、発生するたでたったく気づけないこずです。自動化ゞョブは正垞に動いおいたす。exit code は 0 です。ログもクリヌンです。ただ、人間が Chrome を䜿おうずしたずきだけ、䜕も起きたせん。


䞀般に、この問題は「自動化ゞョブが増えるほど確率が䞊がる」ずいう性質を持っおいたす。ゞョブが1本なら Chrome を掎んでいる時間は短い。3本、5本、10本ず増えるに぀れ、Chrome が垞時いずれかのゞョブに抌さえられおいる状態が垞態化したす。月商を䞊げようずしお自動化を積み増すほど、本䜓の Chrome を䜿えなくなる時間が䌞びる。因果が逆転しおいたす。

解決の方向は2぀考えられたした。䞀぀は自動化ゞョブが Chrome を䜿い終わったら必ず kill する、もう䞀぀は自動化専甚のブラりザを人間甚の Chrome ず名前の段階から分離する。前者はゞョブの終了タむミングを党件管理しなければならず、新しいゞョブを远加するたびに挏れが生たれたす。埌者は䞀床䜜っおしたえば OS レベルで干枉が消える。恒久策は埌者しかありたせん。

/Applications/Chrome Automation.app ずいう名前の別バンドルを䜜り、bundle ID を com.google.ChromeAutomation に倉曎する。OS から芋れば完党に別のアプリです。LaunchServices は com.google.Chrome ず com.google.ChromeAutomation を別々に管理したす。自動化が䜕本 Chrome Automation を立ち䞊げようず、人間の open -a "Google Chrome" は別の bundle ID を探すので圱響を受けたせん。

この仕組みが効く理由を䞀行で蚀えば、「リ゜ヌスの競合は、名前を分けた時点で消える」です。同じ名前を共有しおいる間は、どちらかを殺せばもう䞀方も死ぬ構造になりたす。名前を分ければ、OS が代わりに隔離しおくれたす。


ただし、ここには䞀぀厄介な実装䞊の壁がありたす。

Playwright の channel: 'chrome' オプションは、起動するブラりザのパスを内郚ファむル playwright-core/lib/coreBundle.js に曞いおいたす。このファむルは node_modules の䞭にありたす。npm install を実行するたびに䞊曞きされお /Applications/Google Chrome.app に戻りたす。

぀たり、パスを曞き換えおも定期的に元に戻る。この「戻る」ずいう事実を前提に蚭蚈するこずが、安定運甚の鍵になりたす。「戻るこずを芋越しお、定期的に貌り盎す」。それが6時間ごずの自己修埩スクリプトの存圚意矩です。

党䜓の流れ

システム党䜓の構成を図で瀺したす。

  ┌──────────────────────────────────────────────────────────┐
  │  npm installい぀走るかわからない                          │
  │     ↓                                                     │
  │  playwright-core/lib/coreBundle.js                        │
  │     "...Google Chrome.app/Contents/MacOS/Google Chrome"  │ ← 元に戻る
  └──────────────────────────────────────────────────────────┘
                    ↑最倧6時間以内に怜知
  ┌──────────────────────────────────────────────────────────┐
  │  launchd  com.shun.chrome-automation-repair              │
  │  StartInterval: 216006時間ごずRunAtLoad: true          │
  │     ↓                                                     │
  │  ~/.claude/scripts/chrome-automation-repair.sh           │
  │                                                           │
  │  1. keychain_gate() ─ --use-mock-keychain 未蚭定を譊告      │
  │                                                           │
  │  2. バヌゞョン比范                                           │
  │     src_verGoogle Chrome.app                           │
  │     dst_verChrome Automation.app                      │
  │        䞀臎 → スキップ                                      │
  │        䞍䞀臎 or 䞍圚 → rebuild                            │
  │            cp -R /Applications/Google Chrome.app         │
  │                   /Applications/Chrome Automation.app    │
  │            PlistBuddy: CFBundleIdentifier                │
  │                        → com.google.ChromeAutomation     │
  │            PlistBuddy: CFBundleName                      │
  │                        → Chrome Automation               │
  │            codesign --force --deep --sign -              │ ← ad-hoc 再眲名
  │                                                           │
  │  3. playwright-core を glob で怜玢find より速い           │
  │     ~/dev/*/node_modules/playwright-core/lib/coreBundle.js│
  │     ~/content/*/node_modules/playwright-core/...         │
  │     ※ネスト4階局たで察応                                     │
  │        sed: Google Chrome.app のパス → AUTO_BIN に眮換      │
  │        patched=N  already=M                               │
  │        patched+already==0 → exit 1無音成功を防ぐ          │
  │                                                           │
  │  ログ: ~/.claude/logs/chrome-automation-repair.log        │
  └──────────────────────────────────────────────────────────┘
                    ↓ パッチ枈み
  ┌──────────────────────────────────────────────────────────┐
  │  playwright channel:'chrome' の解決先                      │
  │  /Applications/Chrome Automation.app/                    │
  │              Contents/MacOS/Google Chrome                │ ← 自動化バンドル
  └──────────────────────────────────────────────────────────┘
         ↕ 完党に独立
  ┌──────────────────────────────────────────────────────────┐
  │  open -a "Google Chrome"                                 │
  │  com.google.Chrome ← 人間の Chrome干枉なし               │
  └──────────────────────────────────────────────────────────┘

スクリプトの栞ずなる郚分を実コヌドで確認したしょう。

バンドル䜜成ずバヌゞョン管理

SRC="/Applications/Google Chrome.app"
DST="/Applications/Chrome Automation.app"
BUNDLE_ID="com.google.ChromeAutomation"
AUTO_BIN="$DST/Contents/MacOS/Google Chrome"

ver() { /usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" \
        "$1/Contents/Info.plist" 2>/dev/null; }

src_ver="$(ver "$SRC")"
dst_ver="$(ver "$DST")"

if [ ! -d "$DST" ] || [ "$src_ver" != "$dst_ver" ]; then
  cp -R "$SRC" "$DST"
  /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $BUNDLE_ID" \
      "$DST/Contents/Info.plist"
  /usr/libexec/PlistBuddy -c "Set :CFBundleName Chrome Automation" \
      "$DST/Contents/Info.plist"
  codesign --force --deep --sign - "$DST"
fi

ver() 関数は PlistBuddy で CFBundleShortVersionString䟋127.0.6533.120を読み出したす。Google Chrome が自動アップデヌトするず src_ver ず dst_ver がズレるため、次の6時間サむクルで自動的に Chrome Automation.app を䜜り盎したす。

codesign --force --deep --sign - の -マむナスはad-hoc 眲名を意味したす。Info.plist の bundle ID を曞き換えた時点で Google が付けた眲名は無効になりたす。再眲名なしで起動しようずするずヘルパヌプロセスが SIGKILLexit 137で即死したす。ad-hoc 眲名は信頌チェヌンを持たない自己眲名ですが、ロヌカル実行のみなら問題ありたせん。

playwright-core の glob 怜玢ずパッチ

SCAN_ROOTS=("${HOME}/dev" "${HOME}/content")

list_core_bundles() {
  local root sub
  shopt -s nullglob
  for root in "${SCAN_ROOTS[@]}"; do
    for sub in \
      "$root"/*/node_modules/playwright-core/lib/coreBundle.js \
      "$root"/*/node_modules/playwright/node_modules/playwright-core/lib/coreBundle.js \
      "$root"/*/*/node_modules/playwright-core/lib/coreBundle.js \
      "$root"/*/*/node_modules/playwright/node_modules/playwright-core/lib/coreBundle.js
    do
      [ -f "$sub" ] && echo "$sub"
    done
  done
  shopt -u nullglob
}

~/dev ず ~/content 以䞋のリポゞトリを最倧4階局の glob パタヌンで舐めたす。find . -name "coreBundle.js" で党ツリヌを走査するず4分かかるずころを、実際に存圚するレむアりト*/node_modules/playwright-core/... か */node_modules/playwright/node_modules/playwright-core/... の2パタヌンを盎接 glob で叩くこずで実行時間を 0.95秒 に抑えおいたす。

パッチ適甚郚分は sed -i '' によるむンプレヌス眮換です

patched=0; already=0
while IFS= read -r f; do
  if /usr/bin/grep -q "$SRC/Contents/MacOS/Google Chrome" "$f" 2>/dev/null; then
    /usr/bin/sed -i '' \
      "s|$SRC/Contents/MacOS/Google Chrome|$AUTO_BIN|g" "$f" \
      && patched=$((patched+1))
  elif /usr/bin/grep -q "$AUTO_BIN" "$f" 2>/dev/null; then
    already=$((already+1))
  fi
done < <(list_core_bundles)

if [ $((patched + already)) -eq 0 ]; then
  log "ERROR: playwright-core が1件も芋぀からない。SCAN_ROOTS を確認せよ。"
  exit 1
fi

patched は今回曞き換えた件数、already はすでにパッチ枈みの件数です。䞡方が0の堎合、探玢パスが壊れおいお䜕も芋぀かっおいないこずを意味したす。「0件だけど成功扱い」を防ぐために exit 1 で明瀺的に倱敗させたす。このfail-loud 蚭蚈は埌になっお「なぜ盎っおいないのに6時間気づかなかったのか」ずいう疑問を防ぎたす。

launchd による定期実行

<key>StartInterval</key><integer>21600</integer>
<key>RunAtLoad</key><true/>
<key>LowPriorityIO</key><true/>
<key>Nice</key><integer>10</integer>
<key>ProcessType</key><string>Background</string>
<key>StandardOutPath</key>
  <string>~/.claude/logs/chrome-automation-repair.log</string>

StartInterval: 21600 は 21600秒 = 6時間です。RunAtLoad: true なので launchctl load した瞬間にも䞀床走りたす。Nice: 10 ず LowPriorityIO: true で CPU・ディスクの優先床を䞋げ、バックグラりンドで人間の䜜業を劚げない蚭定にしおいたす。

6時間ずいう間隔の意味は、「npm install が走っおから最倧6時間以内に自動修埩する」ずいう SLA です。実際に自動化ゞョブが npm install を叩く頻床は䜎いため、ほずんどの堎合は次のサむクルが来る前にパッチが戻るこずはありたせん。䞇が䞀戻っおも6時間埌には盎っおいる。この「盎る窓の長さ」を既知のリスクずしお受け入れた蚭蚈です。

Keychain ゲヌトの怜知

スクリプトの冒頭に keychain_gate() ずいう怜知凊理がありたす

keychain_gate() {
  local hits
  hits=$(grep -rIl --include='*.sh' --include='*.py' --include='*.js' \
      --exclude-dir=node_modules ... \
      -E 'Chrome Automation\.app/Contents/MacOS|Google Chrome\.app/Contents/MacOS' \
      "${SCAN_ROOTS[@]}" ~/.claude/scripts 2>/dev/null \
    | xargs -I{} grep -l -e '--headless' {} 2>/dev/null \
    | xargs -I{} grep -L -e 'use-mock-keychain' {} 2>/dev/null \
    | grep -v 'scent-media/scripts/ensure_chrome.sh' || true)
  if [[ -n "$hits" ]]; then
    log "WARNING: --use-mock-keychain 無しでChromeをheadless起動しおいる..."
    echo "$hits" | sed 's/^/  /'
  fi
}

Chrome Automation.app は ad-hoc 眲名なので、macOS Keychain の「Chrome Safe Storage」ACL から「垞に蚱可」を付けおも、Chrome アップデヌトで再ビルドするたびに code hash が倉わっお蚱可が無効になりたす。headless 起動時に --use-mock-keychain --password-store=basic を付けおいないスクリプトは、Keychain ぞのアクセスダむアログが毎回ポップアップする原因になりたす。これが自動化の「無音倱敗」に繋がるため、修正はしないが怜知しお譊告するゲヌトを入れおいたす。

今回の察応で修正したリポゞトリは Playwright の channel:'chrome' 解決先が 16箇所、shell ず Python の盎叩きが 10ファむル、コミットしたリポゞトリは 8本になりたした。芏暡が倧きくなるほど「1箇所盎した」では終わらない。党リポゞトリを暪断しお修正を波及させ、か぀その修正が npm install で消えおも自動で埩掻する仕組みが必芁だったのが、この構成を䜜った理由です。

実装の詳现

pgrep ガヌド——皌働䞭は再ビルドしない

前半で瀺したバンドル䜜成ロゞックには、もう䞀段の安党匁が入っおいたす。バヌゞョン䞍䞀臎を怜知したずき、Chrome Automation.app が動いおいたら再ビルドをスキップする凊理です。

if pgrep -f "Chrome Automation.app/Contents/MacOS" >/dev/null 2>&1; then
  log "SKIP rebuild: 自動化Chromeが皌働䞭 (src=$src_ver dst=${dst_ver:-none})"
else
  log "rebuild: src=$src_ver dst=${dst_ver:-none}"
  rm -rf "$DST" || { log "ERROR: 旧バンドル削陀に倱敗"; exit 1; }
  cp -R "$SRC" "$DST" || { log "ERROR: コピヌ倱敗"; exit 1; }
  ...
fi

cp -R は15〜30秒かかりたす。コピヌ䞭に Chrome Automation.app の Contents/MacOS/Google Chrome が読たれるず、バむナリが䞭途半端な状態で実行され、ゞョブ党䜓が壊れたす。pgrep -f "Chrome Automation.app/Contents/MacOS" で起動䞭のプロセスを探し、居たら「次の6時間サむクルたで埅぀」蚭蚈にしおいたす。

再ビルドをスキップしおも patched/already カりントは埌続で実行されたす。バンドルの実䜓は叀いたたでも、coreBundle.js のパスが正しければゞョブは動き続けたす。

launchd のシェルは人間の PATH を持たない

plist には EnvironmentVariables ブロックが入っおいたす。

<key>EnvironmentVariables</key>
<dict>
  <key>HOME</key><string>~</string>
  <key>LANG</key><string>en_US.UTF-8</string>
  <key>PATH</key>
    <string>~/.local/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>

launchd が起動するシェルは、/etc/launchd.conf から環境を匕き継ぐだけで、~/.zshrc も ~/.zprofile も読みたせん。PATH を明瀺しなければ /usr/bin:/bin しか通っおおらず、/opt/homebrew/bin にある codesign や PlistBuddy が芋぀からない、ずいう状態になりたす。HOME を明瀺するのは ${HOME}/dev ずいう展開がシェル倉数ではなく EnvironmentVariables 経由で解決されるようにするためです。LANG は grep が日本語パスを含むファむル名を壊さないための保険です。

ログの出力先が StandardOutPath ず StandardErrorPath に分かれおいるのも同じ理由です。launchd は暙準出力ず暙準゚ラヌを別ファむルに曞き出し、どちらも ~/.claude/logs/ に萜ずしたす。片方だけ芋おいるず「䜕も起きおいない」ように芋えるこずがありたす。

keychain_gate のパむプラむン読み解き

keychain_gate() の実装は、grep を3段チェヌンする珍しい構造をしおいたす。

grep -rIl ... \
  -E 'Chrome Automation\.app/Contents/MacOS|Google Chrome\.app/Contents/MacOS' \
  "${SCAN_ROOTS[@]}" "${HOME}/.claude/scripts" \
| xargs -I{} grep -l -e '--headless' {} \
| xargs -I{} grep -L -e 'use-mock-keychain' {}

1段目は「Chrome のバむナリパスを盎接曞いおいるファむル」を探したす。node_modules・.git・profiles・logs・venv・tests は探玢から陀倖されおいたす。
2段目は --headless を含むファむルだけを残したす。GUI 起動しおいるスクリプトは Keychain ダむアログが出おも問題ありたせん。
3段目は use-mock-keychain がないファむルだけを残したす-L は「マッチしないファむル䞀芧」フラグ。

この3段を通ったファむルは「headless で Chrome を䜿っおいるのに mock-keychain を付けおいない」ずいう状態のファむルです。そのたびに Keychain の「蚱可したすか」ダむアログがポップアップしたす。自動化䞭にダむアログが出るず、次の click たで党プロセスが止たりたす。scent-media/scripts/ensure_chrome.sh だけ grep -v で陀倖しおいるのは、この1件が別の理由で敢えお本物の Keychain を䜿う蚭蚈になっおいるためです。

スクリプトはこの怜知で自動修正はしたせん。Instagram 等の氞続プロファむルはセッションクッキヌを Keychain の鍵で暗号化しおおり、鍵が倉わるずログむンが吹き飛ぶからです。譊告をログに流しお人間に刀断を委ねる、それが蚭蚈の遞択です。

最終確認コマンド

構成を茉せたら、ここたで実際に動くか確認したす。

# 1. launchd の登録確認
launchctl list | grep chrome-automation-repair

# 2. バンドルが生きおいるか
open -a "Chrome Automation" --args --version

# 3. playwright が正しいパスを芋おいるか
grep -r "Chrome Automation" ~/dev/*/node_modules/playwright-core/lib/coreBundle.js 2>/dev/null | head -5

# 4. 人間の Chrome は独立しお起動できるか
open -a "Google Chrome"

2番の --args --version は Chrome に --version フラグを枡しお起動盎埌のバヌゞョン文字列Google Chrome 127.0.6533.120 などを出力させたす。りィンドりが開かずに文字列だけ返っおきたら、バンドルが正垞に起動できる状態にあるず刀断できたす。4番は -600 を返さずに人間の Chrome が開けば、束瞛の隔離が効いおいるこずの最終確認になりたす。


私が詰たった話

find が4分かかっおいた

最初に曞いたスクリプトは find で探玢しおいたした。

find "${HOME}/dev" "${HOME}/content" \
  -name "coreBundle.js" \
  -path "*/playwright-core/lib/*"

実行しおみるず、完了たで 4分15秒かかりたした。~/dev 配䞋には node_modules が数癟個あり、find はそのすべおのディレクトリを舐めたす。6時間ごずに4分かかる凊理はバックグラりンドずはいえ CPU ずディスクを無駄に消費したすし、䜕より「実際に走らせたら遅い」ずいう事実に最初の実装たで気づいおいたせんでした。

coreBundle.js が存圚するレむアりトは実際には2パタヌンだけです。盎接むンストヌルした playwright-core の堎合は */node_modules/playwright-core/lib/coreBundle.js、playwright 経由でネストしおいる堎合は */node_modules/playwright/node_modules/playwright-core/lib/coreBundle.js。この2パタヌンをリポゞトリ深さ2段たで glob で盎叩きにしたずころ、実行時間は 0.95秒になりたした。

glob が速い理由は、ファむルシステムの走査を OS のディレクトリ゚ントリ参照に萜ずせるからです。find が党ノヌドをたどるのに察し、glob は「このパタヌンに䞀臎する゚ントリが存圚するか」だけを確認したす。探玢範囲が既知の構造に収たっおいるなら、glob は find の代替ずしお明確に優れおいたす。

ad-hoc 眲名を忘れお exit 137

最初の実装では cp -R ず PlistBuddy で曞き換えるずころたで䜜り、codesign を入れおいたせんでした。テストで Chrome Automation.app を起動しようずするず、ヘルパヌプロセスが exit 137 で即死したした。

exit 137 は SIGKILL128 + 9です。プロセスが自分から終了するのではなく、OS が匷制的に殺しおいたす。macOS の Library Validation が、「バむナリは存圚するが眲名が Info.plist の bundle ID ず䞀臎しない」ず刀断したずき、ヘルパヌを SIGKILL したす。Google が付けた眲名は com.google.Chrome に察しお発行されたもので、Info.plist の CFBundleIdentifier を com.google.ChromeAutomation に倉えた時点で無効になりたす。

codesign --force --deep --sign - を远加しお再実行するず起動したした。--force は既存の眲名を䞊曞き、--deep はメむンバむナリだけでなくフレヌムワヌク・ヘルパヌ・プラグむンすべおを再眲名、-マむナスは Apple の蚌明曞を持たない ad-hoc 眲名を指定したす。Gatekeeper はこの眲名を「信頌」したせんが、ロヌカル実行であれば起動できたす。初回だけ「開発元を確認できない」ずいう確認ダむアログが出たすが、システム蚭定の「プラむバシヌずセキュリティ」から「このたた開く」を䞀床抌せば以降は䞍芁です。

探玢0件でも exit 0 で成功扱いになっおいた

初期実装で SCAN_ROOTS を間違っお蚭定したこずがありたす。~/dev ではなく ~/Development ず曞いおしたい、ディレクトリが存圚しないため list_core_bundles が1件も返さない状態になりたした。

このずき、スクリプトは最埌たで正垞終了しお exit 0 を返したした。patched=0・already=0 ですが、どちらの条件にも匕っかからず、ログには playwright patched=0 already=0 rebuilt=0 ず出るだけです。launchd はゞョブの exit code しか芋おいないので、䜕も修正されおいないのに6時間ごずに「成功したした」ず蚘録され続けたした。

npm install でパスが戻った埌、修埩されないたた最倧6時間攟眮されうる、ずいう最悪ケヌスがここで発生したす。patched + already == 0 の堎合に exit 1 で明瀺的に倱敗させるコヌドは、この倱敗から生たれおいたす。

if [ $((patched + already)) -eq 0 ]; then
  log "ERROR: playwright-core が1件も芋぀からない。SCAN_ROOTS を確認せよ。"
  exit 1
fi

launchd は exit code が 0 以倖のずき StandardErrorPath に蚘録し、次のサむクルでも再実行したす。倱敗は隒々しくする。これが正しい蚭蚈です。

ネストが深いリポを1件取りこがしおいた

最初の glob パタヌンは1階局しかカバヌしおいたせんでした。

"$root"/*/node_modules/playwright-core/lib/coreBundle.js

この1段では ~/dev/social-autolike/node_modules/playwright/node_modules/playwright-core/lib/coreBundle.js が匕っかかりたせん。playwright が playwright-core を内包する圢でむンストヌルされおいるケヌスです。social-autolike は playwright を盎接むンストヌルしおいたため、このネストが発生しおいたした。

結果ずしお、他の15箇所は Chrome Automation.app に向いおいるのに、social-autolike だけが Google Chrome.app を盎叩きし続けおいたした。修正埌に dokuji-rosen-sns でも同じ問題が起きたずき埌述、真っ先に「ネストのパタヌンが増えおいないか」を確認する習慣が぀いたのはこの倱敗のおかげです。

preflight が人間の Chrome ごず pkill -9 しおいた

metrics-hub には preflight_chrome.sh ずいうスクリプトがあり、ゞョブ開始前に叀い Chrome プロセスを掃陀する圹割を持っおいたした。このスクリプトの実装が次のようになっおいたした。

pkill -9 "Google Chrome"

pkill はプロセス名に郚分䞀臎したす。Google Chrome ずいう文字列は com.google.Chrome も com.google.ChromeAutomation も䞡方のプロセス名に含たれたす。自動化のゞョブが preflight を叩くたびに、人間が開いおいた Chrome りィンドりが党郚 SIGKILL で殺されおいたした。

さらに悪いこずに、これは修正のきっかけを぀かみにくい壊れ方をしたす。Chrome Automation.app を䜿っおいるゞョブは正垞に動きたす。しかし人間の Chrome が突然萜ちる、ずいう珟象だけが残りたす。ゞョブのログには䜕も残りたせん。「Chrome が萜ちるな」ずいう認識が蓄積されお初めお、preflight が犯人だず気づきたす。

修正は、プロセス名の代わりにバむナリパスで絞り蟌む圢に倉えるこずです。

pkill -9 -f "Chrome Automation.app/Contents/MacOS"

-f はプロセスの起動コマンドラむン党䜓に察しお䞀臎を取りたす。Chrome Automation.app/Contents/MacOS ずいうパスを含むプロセスだけを察象にするこずで、人間の Chrome には觊れない実装になりたす。

4本目のリポが1ヶ月遅れで同じ穎を螏んだ

9月1日の修正で、16箇所の coreBundle.js ず10ファむルの盎叩きを倉曎し、8リポゞトリをコミットしたした。その時点で「党件察応した」ず思っおいたした。

9月11日、dokuji-rosen-sns の tools/lib/browser.mjs が channel:'chrome' のたたになっおいたこずが発芚したす。social-autolike が8月12日に chromium 同梱版ぞ移行した埌も、dokuji-rosen-sns は channel:'chrome' のたた残っおいたした。他のゞョブず Chrome を取り合う朝8時〜10時40分だけ、起動が180秒を超えおタむムアりトする珟象が5日間続いおいたした。5日で39回のハング、8:00枠の125いいねが毎日消えおいたにもかかわらず、アラヌトは「次のサむクルで自動埩垰」の1行で読み流されおいたした。

この教蚓は wiki に残した䞀般圢ず重なりたす。「共通関数の修正は、その関数を䜿っおいる党おの呌び出し元ぞ䌝播しないず意味がない」。スクリプトを盎しおも、同じパタヌンで曞かれた別リポが残っおいる間は修正が完了したずは蚀えたせん。

chrome-automation-repair.sh の SCAN_ROOTS に ~/dev ず ~/content の䞡方を入れおあるのも、片方だけにしか存圚しないリポを芋萜ずさないためです。リポを新しく䜜るたびに「このリポは channel:'chrome' を䜿っおいるか」を確認するのは人間の䜜業です。ただ、スクリプトが6時間ごずに走り patched が1以䞊になれば、少なくずも「取りこがしに気づかないたた1ヶ月が経぀」事態は防げたす。今のずころ、スクリプトが patched=0 already=N を返し続けおいるうちは党件が正しく向いおいるず刀断しおいたす。

぀たずきポむント

実際に螏んだ地雷を網矅したす。「蚭定しおみたら動かない」の倧半はここのどれかです。

  • Info.plist を曞き換えたあず codesign を忘れる。 CFBundleIdentifier を com.google.ChromeAutomation に倉えた時点で Google が付けた眲名は無効になりたす。再眲名なしで起動するず Library Validation に匟かれ、ヘルパヌプロセスが exit 137SIGKILLで即死したす。codesign --force --deep --sign - の --deep は必須です。--force だけではメむンバむナリしか曎新されず、フレヌムワヌク・ヘルパヌ・プラグむンが叀い眲名のたた残りたす。

  • find で node_modules を党走査する。 find ~/dev ~/content -name "coreBundle.js" は完了たで4分15秒かかりたした。~/dev 以䞋の node_modules が数癟個あるためです。coreBundle.js が存圚するレむアりトは「*/node_modules/playwright-core/lib/」ず「*/node_modules/playwright/node_modules/playwright-core/lib/」の2パタヌンだけです。これを glob で盎叩きにするず実行時間が 0.95秒になりたす。既知の構造を党走査する必芁はありたせん。

  • ネストパタヌンを1皮類しか曞かない。 playwright を盎接むンストヌルしたプロゞェクトは */node_modules/playwright-core/lib/coreBundle.js。playwright 経由で playwright-core が入っおいるプロゞェクトは */node_modules/playwright/node_modules/playwright-core/lib/coreBundle.js のネスト圢です。片方しか glob に曞かないず、もう䞀方のリポゞトリを毎回取りこがしたす。social-autolike でこれを螏み、16箇所盎した぀もりが1件だけ旧パスを向いたたたになっおいたした。

  • 探玢0件でも exit 0 で成功扱いになる。 SCAN_ROOTS に存圚しないパスを曞くず list_core_bundles が1件も返さず、patched=0 already=0 のたた正垞終了したす。launchd は exit 0 を「成功」ずしお蚘録し、6時間ごずに「䜕もしない凊理」が静かに走り続けたす。patched + already == 0 のずき exit 1 で明瀺的に倱敗させおはじめお、ログに ERROR が残り次サむクルでも再実行されたす。

  • pkill -9 "Google Chrome" が人間の Chrome ごず殺す。 プロセス名の郚分䞀臎は com.google.Chrome も com.google.ChromeAutomation も䞡方にヒットしたす。metrics-hub の preflight_chrome.sh がたさにこれで、自動化バンドルを掃陀しようずするたびに人間の開いおいるタブを党郚 SIGKILL しおいたした。pkill -f "Chrome Automation.app/Contents/MacOS" ず起動コマンドラむン党䜓で絞るこずで、人間の Chrome には觊れない実装になりたす。

  • launchd のシェルは ~/.zshrc を読たない。 launchd が起動するシェルの PATH は /usr/bin:/bin だけです。/opt/homebrew/bin の codesign も PlistBuddy も芋えたせん。plist の EnvironmentVariables に PATH・HOME・LANG を明瀺しないず、スクリプトが「コマンドが芋぀からない」で黙っお萜ちたす。LANG を en_US.UTF-8 にしないず grep が日本語を含むファむル名を壊す堎合もありたす。ログに䜕も出ないたた exit 127 で終わるため、発芋が遅れたす。

  • 再ビルド䞭に Chrome Automation.app のバむナリを読たれる。 cp -R /Applications/Google\ Chrome.app /Applications/Chrome\ Automation.app は実環境で15〜30秒かかりたす。その間に自動化ゞョブが Chrome Automation.app/Contents/MacOS/Google Chrome を exec しようずするず、コピヌ途䞭のバむナリを掎んでクラッシュしたす。pgrep -f "Chrome Automation.app/Contents/MacOS" で皌働確認し、居たら次の6時間サむクルに延期したす。

  • Keychain ACL は Chrome アップデヌトで無効になる。 Chrome Automation.app は ad-hoc 眲名なので code hash が再ビルドのたびに倉わりたす。macOS Keychain の「Chrome Safe Storage」に「垞に蚱可」を付けおも、Chrome が自動アップデヌトしお再ビルドが走るず ACL が無効になり、headless 起動のたびに「パスワヌドを蚱可したすか」ダむアログが出たす。headless 起動には党件 --use-mock-keychain --password-store=basic を付けるのが正解です。ただし Instagram 等の氞続プロファむルはセッションクッキヌを Keychain の鍵で暗号化しおいるため、mock に切り替えるずログむンが吹き飛びたす。keychain_gate() が怜知しお譊告するだけで自動修正しない蚭蚈は、この理由によりたす。

  • 修正を1リポゞトリだけに留めるず、隣が1ヶ月埌に同じ穎を螏む。 channel:'chrome' を盎接指定しおいるスクリプトは ~/dev ず ~/content の党リポゞトリに散圚したす。1件盎したずころで、別リポゞトリの browser.mjs が叀いパスを向いたたた残り続けたす。実際に dokuji-rosen-sns がこれを螏み、他ゞョブず Chrome を取り合う朝8〜10時40分だけ180秒超ハングする珟象が5日続きたした。5日で39回・8:00枠の125いいねが毎日消えおいたのに、アラヌトが「次 tick で自動埩垰」の1行で読み流されおいたのも蚘録しおおきたす。修正は SCAN_ROOTS の党リポゞトリを暪断確認するたでが1セットです。

  • StandardOutPath ず StandardErrorPath を別々に芋ないず「䜕も起きおいない」に芋える。 launchd は stdout ず stderr を別ファむルに曞き出したす。゚ラヌが .err.log だけに出おいおも .log しか確認しおいなければ気づきたせん。tail -f ~/.claude/logs/chrome-automation-repair.log ず tail -f ~/.claude/logs/chrome-automation-repair.err.log を䞡方確認する習慣を最初から持぀ず、無音倱敗の発芋が数時間から数分に瞮たりたす。

  • Chrome Automation.app の初回起動に Gatekeeper ダむアログが出る。 ad-hoc 眲名は Apple の信頌チェヌンに乗っおいないため、初回だけ「開発元を確認できないため開けたせん」が出たす。システム蚭定→プラむバシヌずセキュリティ→「このたた開く」を䞀床抌せば以降は出たせん。新しい Mac に環境を移した盎埌や、Chrome の倧幅アップデヌト埌バンドルを䜜り盎したタむミングに忘れがちです。


ベストプラクティス

ここたでの構成を汎甚原則ずしお敎理したす。Chrome バンドル分離に限らず、macOS 䞊の長期皌働自動化党般に䜿える蚭蚈指針です。

1. 人間リ゜ヌスず無人ゞョブのリ゜ヌスは「名前」の段階から分ける。
bundle ID・プロファむルパス・ポヌト番号・PID ファむル。どれか䞀぀でも共有した瞬間に「どちらかを殺すずもう䞀方も死ぬ」構造が生たれたす。名前を分けるだけで OS が代わりに隔離しおくれたす。蚭蚈のコストは䞀床きりで、以降は OS が管理したす。

2. 「npm install で必ず戻る」を蚭蚈の前提にする。
パッチを圓おた事実を根拠にしない。「戻るこずを芋越しお定期的に貌り盎す」を圓初から蚭蚈に組み蟌みたす。最倧6時間は旧経路に戻る窓が残りたすが、それは既知のリスクずしお受け入れられたす。「盎した」ず「盎り続ける」は別の状態です。

3. パッチ結果をカりントし、0件を fail-loud にする。
patched=N already=M の2倉数を持ち、patched + already == 0 なら exit 1。「探玢したが䜕もなかった」は「䜕もしなかった」ず同矩です。黙っお exit 0 にするず、蚭定ミスが6時間ごずに静かに繰り返されたす。

4. バヌゞョン比范で冪等性を確保し、䞍芁な再ビルドを省く。
/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" で src_ver ず dst_ver を比范しお「同䞀バヌゞョンなら䜕もしない」を実装するず、6時間ごずのサむクルがほがノヌコストになりたす。Google Chrome が自動アップデヌトしたずきだけ再ビルドが走りたす。

5. 再ビルド前に pgrep ガヌドを入れる。
pgrep -f "Chrome Automation.app/Contents/MacOS" で皌働確認し、居たら次のサむクルに延期したす。コピヌ䞭のバむナリを掎たれる問題は、このガヌドだけで完党に防げたす。「今は觊れない」ず刀断できる仕組みが長期皌働の安定を䜜りたす。

6. launchd plist の EnvironmentVariables に PATH・HOME・LANG を必ず曞く。
むンタラクティブシェルの蚭定を launchd は䞀切読みたせん。手元で動くスクリプトが launchd では黙っお死ぬ、ずいう珟象の倧半はこれです。最初から plist に曞いおおくだけで、この皮のトラブルはれロになりたす。

7. find より glob を䜿い、既知のレむアりトを盎接叩く。
探玢察象の構造が自分でコントロヌルできおいるなら、党走査は垞にやり過ぎです。playwright-core の眮き堎は2パタヌンしかありたせん。glob で盎叩きにするず find の270分の1の時間で終わりたす。

8. codesign は --force --deep --sign - の3オプションセットで䜿う。
--force で既存眲名を䞊曞き、--deep でフレヌムワヌク・ヘルパヌ・プラグむン党䜓に適甚、- で ad-hoc 眲名。1぀でも欠けるず Library Validation が SIGKILL したす。3぀セットで芚えおおけばハマりたせん。

9. headless Chrome には --use-mock-keychain --password-store=basic を付ける。
氞続プロファむルを持぀ゞョブInstagram 等だけは䟋倖ずしお、mock を䜿うずセッションが吹き飛ぶので本物の Keychain を残したす。どちらが該圓するかを把握した䞊で、headless 起動の倧半は mock を付けるのが正しい蚭定です。

10. pkill は -f でフルパス䞀臎にする。プロセス名の郚分䞀臎は䜿わない。
pkill "Google Chrome" は bundle ID が違っおいおも䞡方殺したす。-f "Chrome Automation.app/Contents/MacOS" ず起動コマンドラむン党䜓で絞るこずで、人間の Chrome に觊れない実装になりたす。

11. 修正したら SCAN_ROOTS の党リポゞトリを暪断確認する。
1リポゞトリ盎しおも、同じパタヌンで曞かれた別リポゞトリが残っおいる間は修正が完了しおいたせん。grep -r "channel:'chrome'" ~/dev ~/content --include="*.mjs" --include="*.js" 2>/dev/null で挏れを出し、党件察応しおはじめお完了です。

12. 最終確認コマンドをスクリプトずセットで残す。
蚭定埌に䜕で確認するかを最初から決めおおきたす。今回の構成は次の4コマンドです。

# 1. launchd ぞの登録確認
launchctl list | grep chrome-automation-repair

# 2. バンドルが起動できる状態か
open -a "Chrome Automation" --args --version

# 3. playwright が正しいパスを芋おいるか
grep -r "Chrome Automation" ~/dev/*/node_modules/playwright-core/lib/coreBundle.js 2>/dev/null | head -5

# 4. 人間の Chrome は独立しお起動できるか
open -a "Google Chrome"

2番の --args --version が Chrome のバヌゞョン文字列䟋Google Chrome 127.0.6533.120を返し、4番が -600 なしで開けば、隔離が正しく効いおいたす。「構成を入れたら確認コマンドたで蟌みで1セット」ずいう発想で、半幎埌に同じ環境を再珟するコストを䞋げたす。

13. ログは ~/.claude/logs/ に集め、stdout ず stderr を䞡方確認する。
どちらかだけ芋おいるず、゚ラヌが .err.log に出おいるのに気づかない状態が続きたす。tail -f ~/.claude/logs/*.log ~/.claude/logs/*.err.log で党ゞョブの状態を䞀芧できるようにしおおくず、無音倱敗の発芋コストが䞋がりたす。

14. 修埩窓SLAを意図的に遞び、その根拠を残す。
StartInterval: 216006時間は「npm install が走っおも最倧6時間以内に修埩される」ずいう SLA です。短くすれば確実性が䞊がる䞀方、CPU ずディスクぞの圱響も増えたす。自分の環境で蚱容できる最倧倀を意図的に遞んで、その根拠を plist のコメントに残しおおくこずが重芁です。


たずめ

open -a "Google Chrome" が -600 を返した9月1日の午埌から、蚭蚈ずしお残ったものを䞀行で蚀えば、「競合はリ゜ヌスを共有しおいる間だけ起きる。名前を分ければ OS が代わりに隔離しおくれる」です。

自動化が1本のずきは問題が芋えたせん。2本、5本、10本ず積み䞊がるに぀れお、人間甚リ゜ヌスず自動化リ゜ヌスが同じ名前空間を占有し合い、どちらかを殺せばもう䞀方も壊れる構造が静かに出来䞊がりたす。月商を䌞ばすために自動化を積み増すほど、自分の䜜業環境が壊れる——その逆説に気づいたのが、この蚭蚈のスタヌト地点でした。

npm install で戻るずいう事実を「盎さなければならない問題」ずしお芋るのをやめ、「戻るこずを前提にした定期修埩」ずしお受け入れた時点で、仕組みは安定したした。パッチが戻っおも6時間以内に盎る。その修埩窓を既知のリスクずしお蚭蚈に組み蟌むこずが、長期皌働の鍵です。

chrome-automation-repair.sh が6時間ごずに走り、patched=0 already=16 rebuilt=0 を返し続ける今、自動化ゞョブは Chrome Automation.app を䜿い、人間の Chrome は独立しお動いおいたす。9月1日のような朝は、もう来たせん。


仕組みの党䜓像・月120䞇の内蚳・30日手順は有料noteにたずめおいたす。

📕 Claude Code自埋環境で、実際どう皌ぐか ― 仕組み・実䟋・始め方・サポヌト


Lily@bokuwalily― 個人開発者。Claude Code で自動化基盀を組みながら、iOSアプリやWebサヌビスを量産しおいたす

  • 制䜜物・蚘事は bokuwalily.com にたずめおいたす🖥
  • AIで「寝おおも回る仕組み」を䜜っお月120䞇にした話は noteの有料蚘事 に💰
  • OSS: github.com/bokuwalily 🐙
  • 最新情報・お問い合わせは X @bokuwalily ぞ🌍
  • AI導入・自動化の盞談ず実装テンプレ7本の配垃は 公匏LINE から💬

皆さんの ❀ やシェアが励みになりたす

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?