こんにちは、エンジニアのオオバです。

お悩みさん
お悩みさん
  • dotfilesの.gitconfigに身に覚えのない差分がある
  • safe.directoryが同じ行で何十回も重複している
  • self-hostedランナーを動かしているMacで起きた
  • オオバ
    オオバ
    本記事ではこれらの悩みを解決します。

    Mac mini の dotfiles を origin/main に揃えようとしたら、.gitconfig に +141行 のローカル差分が出てきました。

    自分では1行も書いていません。

    「え、誰が書いたの?」って一瞬ゾッとしますよね。

    犯人は、同じ Mac mini で動かしていた GitHub Actions の self-hosted ランナーでした。

    ~/.gitconfigがsymlinkだとactions/checkoutの「一時HOMEへのコピー」もリンクのままになり、safe.directoryの追記がdotfiles本体に届く。set-safe-directory: falseで止まる

    先に結論をまとめます。

    dotfilesの.gitconfigにsafe.directoryが溜まる原因と対策

    ①actions/checkoutは~/.gitconfigを一時HOMEにコピーしてから、そこへ--globalでsafe.directoryを書く

    ②コピーはsymlinkをリンクのまま複製するので、追記がdotfilesの.gitconfigに届く

    ③ランナーと作業ディレクトリの所有者が同じなら、set-safe-directory: falseにすれば追記が止まる

    ざっくり言うと、前半が症状と原因、後半が直し方とその判断です。

    では本編に入っていきます。

    →11万文字で徹底解説した「DOTweenの教科書」Unityアニメーションの超効率化ツールはこちら

    起きたこと:同じ1行が140回重複していた

    差分の中身は、全部同じ1行でした。

    safe.directory = /Users/ohbashunsuke/workspace/actions-runner/mf-getter/_work/mf-getter/mf-getter  
    

    この行が 140回 重複して、.gitconfig の末尾に積み上がっていたわけです。

    パスを見れば、self-hosted ランナーの作業ディレクトリだとすぐ分かります。

    放っておくと定期同期で衝突する

    僕の Mac mini では、dotfiles-sync という launchd のジョブが毎日12:00に動いています。

    ローカル変更を stash して、fast-forward して、stash を戻す流れです。

    そのため、origin 側で .gitconfig の末尾を変えた日に stash を戻すと衝突し、#mm-alert(アラート用の Slack チャンネル)が鳴ります。

    つまり、ランナーが書き足すたびに地雷を1個ずつ埋めていたということ。

    環境

    最後の1行が今回の主役です。

    原因:一時HOMEへのコピーがsymlinkのままだった

    checkout の実行ログを見ると、次の4行が出ていました。

    Copying '/Users/ohbashunsuke/.gitconfig' to '.../_work/_temp/<uuid>/.gitconfig'  
    Temporarily overriding HOME='.../_work/_temp/<uuid>' before making global git config changes  
    Adding repository directory to the temporary git global config as a safe directory  
    [command]/usr/bin/git config --global --add safe.directory .../_work/mf-getter/mf-getter  
    

    ログの流れはこうです。

    1. ~/.gitconfig を一時ディレクトリにコピーする
    2. HOME をその一時ディレクトリに差し替える
    3. git config --global --add safe.directory で作業ディレクトリを登録する

    checkout は、本物の ~/.gitconfig を汚さないように作られています。

    一時 HOME にコピーして、そちらに --global で書く。

    設計としては丁寧なんですよね。

    コピーがリンクをリンクのまま複製していた

    ところが、このコピー(@actions/io の cp)は symlink をリンクのまま複製します。

    僕の ~/.gitconfig は dotfiles を指す symlink です。

    そのため、一時 HOME に置かれた .gitconfig も dotfiles を指すリンクになっていました。

    「一時」のはずの追記が、リンクをたどって dotfiles 本体に書き込まれていたわけです。

    身近な物で例えると、書類のコピーを取ったつもりが、実は原本の置き場所を書いた付箋をコピーしていた状態。

    付箋のコピーに書き込もうとすれば、結局は原本に書き込むことになります。

    後片付けでは行が消えない

    checkout の後片付けは、一時ディレクトリを消すだけです。

    一時ディレクトリにあるのはリンクなので、消えるのはリンクだけ。

    dotfiles に追記された行はそのまま残ります。

    ワークフローは毎日2本動いていたので、実行のたびに1行ずつ増えて140行になったということです。

    解決:set-safe-directory: falseを指定する

    checkout に set-safe-directory: false を渡しました。

    - name: Checkout
      uses: actions/checkout@v4  
      with:  
        set-safe-directory: false  
    

    「safe.directory を切って大丈夫なの?」と思うかも知れません。

    結論、僕の環境では要りませんでした。

    git が safe.directory を求めるのは、リポジトリの所有者と git を実行するユーザーが違うときだけです。

    ランナーは作業ディレクトリの所有者と同じユーザーで動いているので、もともと git に怒られる状況になりません。

    動作確認

    ブランチ上で workflow_dispatch から実行し、次の3点を確かめました。

    ちなみに dotfiles に溜まっていた140行は、ランナー側を直しても消えません。

    origin/main に揃えるときに捨てる必要があります。

    他の選択肢と、選んだ理由

    直し方は他にも2つ考えました。

    どちらも効くと思いますが、ランナー以外のツールや普段の作業にも影響が出ます。

    checkout の設定1行なら、影響はこのワークフローの中だけで済みます。

    なので、いちばん範囲が小さい方法を選びました。

    オチ:ランナーごと止めた

    直して PR もマージしたあとで、mf-getter 自体をもう使っていないことに気づきました(笑)。

    結局、同じ日にランナーごと停止しています。

    とはいえ、原因の仕組みは他のツールでも起こり得る話なので、記録として残しておきます。

    補足:symlinkでdotfilesを配っている人は要注意

    今回の学びは「一時 HOME にコピーするから安全」という設計も、元ファイルが symlink だと素通りになるということ。

    dotfiles を symlink で配っていると、CI や自動化ツールが ~/.gitconfig に --global で書いた内容は、そのまま dotfiles の差分になります。

    self-hosted ランナーを普段使いの Mac で動かしているなら、一度 dotfiles の git status を見てみてください。

    身に覚えのない行が増えていたら、今回と同じ仕組みを疑うのが近道です。

    まとめ

    まとめ

    ①~/.gitconfigがsymlinkだと、actions/checkoutの一時HOMEへのコピーもリンクのままになる

    ②その結果、safe.directoryの追記がdotfilesの.gitconfigに実行のたびに1行ずつ溜まる

    ③ランナーと作業ディレクトリの所有者が同じなら、set-safe-directory: falseで止められる

    自分で書いた覚えのない差分って、見つけた瞬間かなり焦りますよね。

    ただ、ログを順番に追えば原因はちゃんと書いてあります。

    同じ症状で困っている人の近道になればうれしいです。

    オススメ記事