Skip to content
nrhrhysd616Public

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

1 watching

Forks

Repository files navigation

nrhrhysd616/dotfiles

macOS環境構築

シェル環境構築

  1. 以下のアプリケーションをインストール&一度起動しておく

  2. 以下のZshコマンドを順番に実行

    $git clone git@github.com:nrhrhysd616/dotfiles.git
    $chmod 744 dotfiles/init-mac.zsh
    $zsh dotfiles/init-mac.zsh
  3. iTerm2の設定を反映

    iTerm2を起動しメニューの「Settings...」から以下を設定
    General -> Settings -> Import All Settings And Data...を選択
    dotfiles/iterm2/iTerm2-all-settings.itermexportのファイルを選択してインポート

  4. .gitconfig.user.localファイルを編集

    • name: アルファベットフルネーム
    • email: Githubで利用しているメールアドレス

    user.signingkeyとgpg.ssh.programは端末ごとに絶対パスが変わるため、 次の手順のgen-mac-git-signkeyが書き込む(手で設定しない)

  5. コミット署名用の鍵をSecure Enclaveに用意する

    init-mac.zshが既に一度実行しているが、手順4でメールアドレスを設定した後に もう一度実行して、GitHubへの鍵登録と署名者リストへの追記まで済ませる

    gen-mac-git-signkey

    ghのトークンに鍵登録用のスコープが無い場合は案内が出るので、追加してから再実行する

    gh auth refresh -h github.com -s admin:ssh_signing_key,admin:public_key
    gen-mac-git-signkey

    この端末の公開鍵がgit/allowed_signersへ追記されるので、commitしてpushする
    そうすると他の端末でもこの端末が作った署名を検証できるようになる

    仕組みと運用は後述のコミット署名とSSH認証(Secure Enclave)を参照

  6. sshdを再起動して設定を反映する

    sudo launchctl stop com.openssh.sshd
    sudo launchctl start com.openssh.sshd
  7. VSCodeのClineのCustom Instructionsを手動で設定する

    現状設定のエクスポートなどができず、settings.jsonファイルでの外出しも不可能なため手動

  8. Tailscaleにサインインし、tailnetのHTTPS証明書を有効化する

    後述のcsctlコマンドを使う場合のみ必要

    tailscale up

    サインイン後、管理コンソールのDNS設定で 「HTTPS Certificates」をEnable HTTPSにする
    これを有効にしないとtailscale certが証明書を発行できず、csctlが起動に失敗する

    サインインさえ済ませておけば接続は切っておいてよい
    csctlが必要なときだけ自動で接続し、使い終わったら自動で切断する

  9. AWS CLIの認証設定

    aws/configからコピーされた~/.aws/configを編集し、login_sessionにIAMユーザーのARNを設定する

    [default]
      login_session = arn:aws:iam::<account-id>:user/<iam-user-name>
      region = ap-northeast-1

    認証はaws loginで行う(AWS CLI 2.32.0以降が必要)
    ブラウザでコンソールにサインインすると一時クレデンシャルが~/.aws/login/cacheに発行される
    有効期限は最長12時間で、その間15分ごとに自動更新される

    aws login
    aws sts get-caller-identity  # ARNを確認

    この方式では長期のアクセスキーを一切作らないため、~/.aws/credentialsは存在しない

    サインインにはMFAを設定したIAMユーザーを使うこと
    ルートユーザーはアカウント解約・支払い情報の変更などルート専用の操作にのみ使い、 日常の操作やCLIからは使わない

コミット署名とSSH認証(Secure Enclave)

コミット署名とGitHubへのSSH認証には、macOSのSecure Enclave内に作った鍵を使います。 秘密鍵はハードウェアから取り出せず、sc_auth create-ctk-identity -t noneで作るため 署名のたびに生体認証を求められません。コミット1回あたり0.1秒程度で、 Claude等のコーディングエージェントに無人で作業させても承認待ちで止まりません。

要素 役割
bin/gen-mac-git-signkey 鍵の生成、GitHubへの登録、gitconfigへの反映、状態確認
bin/mac-ssh-keygen gitが署名時に呼ぶssh-keygenラッパー(Secure Enclaveのプロバイダを渡す)
git/allowed_signers 全端末の公開鍵を集めた署名者リスト(git log --show-signature用)
ssh/config GitHubへの認証にSecure Enclaveの鍵を使う設定
~/.ssh/id_git_sign ハードウェア内の鍵を指すハンドル(秘密鍵そのものではない)
  • 鍵は端末ごとに別になる

    Secure Enclaveの秘密鍵はエクスポートできないため、~/.ssh/id_git_signを他の端末へ コピーしても機能しません。端末ごとに鍵を作り、GitHubには端末ごとに署名キーと 認証キーを登録します。鍵のパスとgitconfigの構造は全端末で同じなので、 運用上の違いは出ません

  • 状態を確認する

    gen-mac-git-signkey --status

    鍵、証明書の残り有効期間、gitconfigの設定値、GitHubへの登録状況、署名テストの結果が出ます

  • 鍵を作り直す

    gen-mac-git-signkey --recreate

    sc_authが発行する自己署名証明書の有効期間は1年しかないため、期限が近づいたら 作り直します(--statusが残り90日を切ると警告します)

    作り直したあとも、GitHub上の古い鍵とgit/allowed_signersの古い行は消さないこと
    消すとその鍵で署名した過去のコミットがUnverifiedになります

  • 1Passwordの署名に戻す

    旧鍵とop-ssh-signは残してあるので、~/.gitconfig.user.localと~/.ssh/configを戻せば復帰できます

    git config --file ~/.gitconfig.user.local gpg.ssh.program "/Applications/1Password.app/Contents/MacOS/op-ssh-sign"
    git config --file ~/.gitconfig.user.local user.signingkey "<1Passwordに入れている公開鍵の文字列>"
    # ~/.ssh/config は init-mac.zsh が作ったバックアップ(config.bak.<timestamp>)から復元する
  • VSCodeなどGUIから操作したときに出るプロンプトについて

    FIDO鍵は署名・認証の直前にConfirm user presence for key ...という通知を出します。 標準エラーが端末ならそこへ1行出るだけですが、端末でない場合OpenSSHはSSH_ASKPASSの プログラムを起動してこれを表示しようとします。VSCodeやcode-serverはSSH_ASKPASSを 自前の入力ボックスに設定しているため、答える必要のない通知が毎回プロンプトとして現れます (VSCodeがメッセージをパスワード要求として解釈するので"key" has fingerprint ""のような 崩れた表示になります)

    -t noneで作った鍵はSecure Enclaveが即座に応答するので確認する相手はいません。 git/.gitconfigのcore.sshCommand(fetch/push用)とbin/mac-ssh-keygen(署名用)の 両方でSSH_ASKPASS_REQUIRE=neverを設定して抑止しています。 手動のsshコマンドには影響しないため、新しいホストの鍵確認は従来どおりできます

  • セキュリティ上の前提

    秘密鍵は取り出せませんが、-t noneで作った鍵はその端末で動くプロセスなら誰でも署名に使えます。 端末そのものが乗っ取られた場合は署名を偽造されうるので、そこは端末の安全性に依存します

シークレット検査(gitleaks)

gitleaksで、APIキーやトークンがコミットに混ざるのを機械的に止めています。

.gitignoreやClaude Codeのdeny設定が防げるのは「特定のファイル名・パス」だけで、 ソースコードにベタ書きされた値の中身までは見ていません。gitleaksがその穴を埋めます。 前述のとおり署名鍵は-t noneで作ってあり、コーディングエージェントが無人でコミット・pushできます。 人間のレビューを挟まずに履歴が公開へ流れる経路がある以上、検査は仕組みとして持たせておく必要があります。

検査は2箇所で走ります。

どこで 何を見るか すり抜けられるか
ローカルのpre-commitフック ステージ済みの差分 --no-verifyで飛ばせる
GitHub Actions(.github/workflows/gitleaks.yml) プルリクエストの差分 飛ばせない

CIをプルリクエストだけに絞っているのは、pushでも走らせると同じコミットを二度検査することになり、 かといってmainへのpushに絞ると検出がマージ後になって遅いためです。マージ前に止めるのが目的なので、 mainへ直接pushした場合はCIが走りません(その場合の防壁はローカルのフックだけになります)。

このマシンの全リポジトリで効く

フックの実体はgit/hooks/pre-commitで、init-mac.zshが~/.git-hooksへ ディレクトリごとシンボリックリンクを張ります。git/.gitconfigのcore.hooksPathが そこを指しているため、dotfilesに限らずこのマシンの全ローカルリポジトリで コミット前の検査が走ります。

core.hooksPathはpathnameとして扱われるため~/が展開されます (gpg.ssh.programは展開されず絶対パスが要る。同じgitconfigでも扱いが違う点に注意)。 実際の解決先は次のコマンドで確認できます。

git rev-parse --git-path hooks
# /Users/<user>/.git-hooks

効かないケースが2つあります。

  • 各リポジトリの.git/hooksは無効化されます。core.hooksPathは探索先を丸ごと差し替えるためです
  • husky・lefthookを使うプロジェクトでは走りません。これらはリポジトリ側でcore.hooksPathを 設定するので、local設定がglobal設定に優先します。そこでの最後の砦はそのプロジェクトのCIになります

gitleaksが入っていない端末では、警告だけ出してコミットは通します。 dotfilesを適用していないマシンでコミットが一切できなくなるのを避けるためです。

既存の端末ではinit-mac.zshを再実行してください。 gitleaks本体のインストールと ~/.git-hooksのリンクはそこで行われます。git/.gitconfigはシンボリックリンクなので pullした時点でcore.hooksPathは効きますが、リンク先が無い間はフックが存在しない扱いになり、 警告も出ないまま検査だけが走らない状態になります。次のコマンドで確認できます。

ls -l ~/.git-hooks && gitleaks version

検出されたとき

コミットが中断され、検出したファイル・行・ルールIDが表示されます。 値そのものは--redactでマスクされるため、端末やログに秘密は残りません。

Finding:     token = REDACTED
RuleID:      github-pat
File:        example.txt
Line:        1
Fingerprint: example.txt:github-pat:1

本物のシークレットなら、混入した箇所を消してからコミットし直します。 誤検知だった場合の逃がし方は3つあります。

方法 書き方 効く範囲
インラインコメント 検出された行の末尾にgitleaks:allow その1行
.gitleaksignore 表示されたFingerprintの値を1行書く その検出1件
.gitleaks.toml [[rules.allowlists]]にパスや正規表現を書く パターン全体

いずれもリポジトリのルートに置き、CIとローカルの両方で効きます。

コーディングエージェントの設定共有

Claude CodeとCodexに、同じルール・ナレッジ・スキルを全端末で共有させています。 実体はagents/配下の1箇所だけで、init-mac.zshが両エージェントの探索先へシンボリックリンクを張ります。

3つの置き場

用途によって置き場を分けます。判断基準は「毎セッション必ず効かせたいか」です。

置き場 何を置くか 読み込まれ方
agents/rules/ 短い恒久的な行動ルール 毎セッション全文
agents/knowledge/ 参照用の長いナレッジ 索引だけ。本文は必要になったときだけ読まれる
agents/skills/ 手順・ワークフロー 名前と説明だけ。本文は起動時

rules/は全文が毎回コンテキストに乗るので、長い解説やたまにしか要らない知識はknowledge/へ置きます。 索引に書く1行には「いつ読むか」を必ず含めます。書名だけでは呼び出されません。

3つの公開範囲

このリポジトリはpublicです。 業務・個人固有のナレッジは別のprivateリポジトリに置きます。

層 実体 端末間同期
shared このリポジトリ される
private agent-knowledge-private(privateリポジトリ) される
local ~/.agents/knowledge-local/ されない

local層をリポジトリの外に置いているのは、publicリポジトリでgit clean -xdfを打ったときに 業務ナレッジを巻き添えで消さないためです。

スキルは2つの層のものを1つの並びに混ぜるため、ディレクトリ全体ではなくスキルごとに ~/.claude/skills/<name>と~/.agents/skills/<name>へリンクします。

新しい端末ではinit-mac.zshがagent-knowledge-privateをghq getしますが、 アクセスできない場合は警告を出してprivate層を空のままにし、初期化は続行します。

追加・更新

エージェントに「これを覚えて」「今後は〜して」と頼むと、knowledgeスキルが起動して 層と置き場を判定し、索引への追記まで行います。sharedへ書く前に組織名・ホスト名・ 認証情報が混ざっていないか確認する手順が入っています。

変更をpushしないと他の端末には反映されません。

2つのエージェントの違い

スキルは形式が同一なので実体を共有できますが、ルールの自動ロードは仕組みが異なります。

Claude Code Codex
スキル置き場 ~/.claude/skills/ ~/.agents/skills/
グローバル指示 ~/.claude/CLAUDE.md + ~/.claude/rules/**/*.md ~/.codex/AGENTS.md 1ファイルのみ
ファイル分割 @pathによるimport できない

Codexはimport機構を持たないため、AGENTS.mdには「置き場の地図」だけを書き、 ルール本文はコマンドを明示してその場で読ませています。 Claude Codeがrulesを自動ロードするのに対し、Codexでは指示に留まるぶん遵守率は落ちます。

miseの使い方

miseは複数の言語のバージョン管理ツールです。Python、Node.js、pnpm、Bun、Go、Ruby、Terraformをmiseで管理しています。

  • インストール済みツールの確認

    mise list
  • 利用可能なバージョンの確認

    mise ls-remote python
    mise ls-remote node
  • グローバルバージョンの変更

    # 特定バージョンを指定
    mise use -g python@3.12
    mise use -g node@20
    
    # 最新版にアップデート
    mise use -g python@latest
    mise use -g node@lts
  • プロジェクトごとのバージョン指定

    # プロジェクトディレクトリで実行
    cd /path/to/project
    mise use python@3.11  # mise.tomlが生成される
    mise use ruby@3.3     # プロジェクトのRubyを固定したい場合

    グローバルはlatestで運用しているため、バージョンを固定したいプロジェクトはこの方法で指定する

  • rbenv・nodenv由来のバージョンファイルについて

    miseは.ruby-versionや.node-versionをデフォルトでは読まない
    これらが置いてあるプロジェクトでもグローバルのバージョンが使われるため、 尊重させたい場合はツールごとに有効化する

    mise settings add idiomatic_version_file_enable_tools ruby
  • 現在のバージョン確認

    python --version
    node --version
    bun --version

CocoaPodsの使い方(iOSプロジェクト)

CocoaPodsはグローバルにインストールせず、プロジェクトのGemfileでバージョンを固定します。
プロジェクトごとにpodのバージョンがズレても衝突せず、sudo gem installも不要になるためです (init-mac.zshが用意するのはmise管理のRubyまで)。

  • Expoプロジェクト(expo run:ios)の場合

    ルートにGemfileを置くだけでよく、podコマンドを自分で叩く必要はありません

    cd /path/to/expo-project
    
    bundle init            # Gemfileを生成
    bundle add cocoapods   # GemfileとGemfile.lockにバージョンが記録される
    
    npx expo run:ios       # prebuildでios/を生成し、pod installまで自動実行される

    Expo CLIはgitルートまでの親ディレクトリからGemfileを探し、gem "cocoapods"の記述があって bundle exec pod --versionが通る場合にbundle exec pod installを使います
    Gemfileが無い場合はPATH上のpodを探し、見つからなければsudo gem install cocoapodsを促してきます

    Podfileはexpo prebuildがios/の中に生成するためpod initは不要です
    プロジェクトルートでpod installを実行するとNo 'Podfile' foundになります

  • Podfileを自分で管理するプロジェクトの場合

    cd /path/to/ios-project
    
    bundle init
    bundle add cocoapods
    
    bundle exec pod init      # Podfileを生成(.xcodeprojと同じ階層で実行)
    bundle exec pod install

    Gemfile・Gemfile.lock・Podfile・Podfile.lockをコミットする

  • 既にGemfileがあるプロジェクト

    bundle install         # Gemfile.lockのバージョンでCocoaPodsが入る
    bundle exec pod install
  • gemの実体もプロジェクト配下に隔離する場合

    上記だけでもバージョンはGemfile.lockで固定されますが、gem自体はmise管理Rubyの共有gemディレクトリに入ります
    完全に分けたい場合は次のように設定します

    bundle config set --local path vendor/bundle  # .bundle/configが生成される
    bundle install
    echo "vendor/bundle" >> .gitignore

    .bundle/configはコミットしてよい(チーム全員が同じ配置になる)

注意:

  • 手でpodを叩くときはbundle execを付ける。グローバルにpodを入れていないため、 素のpod installはcommand not foundになる(バージョンを取り違えないための安全弁)
  • Rubyのバージョンを固定したい場合はmise use ruby@3.3でmise.tomlを生成する
    miseは.ruby-versionをデフォルトでは読まない(前述のmiseの使い方を参照)
  • 指定したバージョンにprecompiledバイナリが無いと、miseがソースビルドを行うため数分かかる
    ビルドに必要なopenssl@3とlibyamlはinit-mac.zshが導入済み

copyの使い方

copyはファイルや標準入力の内容をクリップボードへ入れるコマンドです。 pbcopy < README.mdのようにリダイレクトを書き分けなくても、copy README.mdとパスを渡すだけで済みます。

クリップボードへの書き込みには環境に応じたコマンドが選ばれます(macOSはpbcopy、Waylandはwl-copy、 X11はxclipかxsel、WSLはclip.exe)。特定のOS専用ではないためmac-のようなプレフィックスは付いていません。

  • ファイルをコピーする

    copy README.md              # 1つのファイル
    copy src/a.ts src/b.ts      # 複数指定すると連結される
    copy -H src/*.ts            # 各ファイルの前に "==> src/a.ts <==" の見出しを付ける

    複数のコードをAIやチャットへ貼るときは-Hを付けると、貼った先でもファイルの境目が残ります。

  • コマンドの出力をコピーする

    git diff | copy             # 標準入力から読む
    copy - README.md < head.txt # "-" は標準入力を表す。ファイルと混ぜられる
  • その他

    copy -q README.md           # 完了行を出さない
    copy --help                 # 使い方

    コピーすると✓ copied 42 lines, 1.2 KB from README.mdのように結果が1行出ます。 この表示もエラーもすべて標準エラーへ出るため、標準出力を使うパイプは汚れません。

補足:

  • 指定したパスはすべて検査してから書き込みます。copy a.txt typo.txtのように一部が読めない場合は 何もコピーせずに終了するので、その時点のクリップボードは壊れません
  • 空の入力は拒否します。 差分の無いgit diff | copyでクリップボードが黙って空になり、 直前にコピーした内容を失うのを避けるためです(pbcopyはこの場合クリップボードを空にします)
  • 貼り付け側のコマンドは用意していません。pasteはPOSIX標準コマンド(/usr/bin/paste、複数ファイルの行を 横に連結する)として実在し、$HOME/.local/binがPATHの先頭にあるため、同名を置くと標準のpasteを隠します。 貼り付けはpbpasteを使ってください

csctlの使い方

csctlは任意のプロジェクトディレクトリをcode-server(ブラウザ版VS Code)として起動し、 Tailscale経由で公開するコマンドです。iPadなど同じtailnetに参加している端末のブラウザからプロジェクトを開けます。

各インスタンスはこのマシンのTailscale IPに直接バインドし、tailscale certが発行した証明書でTLSを終端するため、 https://<MagicDNS名>:<ポート>/でアクセスできます。インターネットには一切公開されません。

  • 起動して公開する

    # ディレクトリを指定するだけ(ポートは18080から空きを自動選択、インスタンス名はディレクトリ名)
    csctl start ~/Documents/Project/github.com/nrhrhysd616/dotfiles
    
    # カレントディレクトリを公開する
    csctl start
    
    # ポートと名前を明示する(複数プロジェクトを同時に公開できる)
    csctl start ~/work/api --port 18090 --name api

    起動に成功するとURLとパスワードが表示されます。パスワードは全インスタンス共通で、初回に自動生成されます。

  • 一覧・状態・ログ

    csctl list             # 全インスタンスの一覧
    csctl status api       # 1つの詳細(URL、PID、ログの場所など)
    csctl logs -f api      # ログを追尾
    csctl url api          # URLだけを出力
    csctl open api         # ローカルのブラウザで開く
  • 停止

    csctl stop api         # 名前で停止
    csctl stop 18090       # ポートで停止
    csctl stop all         # 全部停止
    csctl restart api      # 同じディレクトリ・ポートで再起動
  • その他

    csctl doctor           # 依存コマンドとTailscaleの状態を確認
    csctl password         # 共通パスワードを表示
    csctl password --reset # パスワードを再生成(再起動するまで既存インスタンスは旧パスワード)
    csctl cert             # TLS証明書の発行・更新
    csctl font install     # Fira Codeをcode-server自身から配信する(後述)
    csctl help             # 全コマンドのヘルプ
  • エディタ設定とテーマ

    設定はcode-server/settings.jsonで管理し、csctlが各インスタンスの User/settings.jsonからシンボリックリンクを張るため全インスタンスで共有される。 code-server上で設定を変更するとリポジトリ側のファイルが直接書き換わる。

    vscode/settings.json(デスクトップ版)とは別ファイルにしている。ブラウザ版では 拡張機能を入れ直す必要があり、外部アプリ連携も動かないため、 拡張機能に依存しない設定だけを持たせている。

    • テーマは組み込みのMonokaiとアイコンテーマvs-setiを使う(拡張機能不要)
    • Monokai Proを使いたい場合はcode-server上で拡張機能monokai.theme-monokai-pro-vscodeを インストールし、code-server/settings.jsonのworkbench.colorThemeを書き換える
    • 拡張機能は~/.local/share/csctl/extensionsに置かれ全インスタンスで共有されるため、 インストールは一度だけでよい
  • Fira Codeを全端末で使う

    editor.fontFamilyはブラウザが動いている端末のフォントを参照するため、 Fira Codeが入っていないiPadなどでは効かずフォールバックされる。 以下を実行するとcode-server自身がWebフォントとして配信するようになり、どの端末でも適用される。

    csctl font install   # 有効化(実行後、起動中のインスタンスはrestartが必要)
    csctl font status    # 状態確認
    csctl font remove    # 元に戻す

    この処理はcode-server本体のCSSに@font-faceを追記し、~/Library/FontsのFira Codeを 配信ディレクトリへコピーする。code-serverを更新すると失われるが、 一度有効にしておけば次回のcsctl start時に自動で貼り直される。

  • 動作のカスタマイズ

    環境変数 説明
    CSCTL_PORT_START ポート自動選択の開始番号(既定: 18080、そこから20個スキャンする)
    CSCTL_AUTO_UP 0を指定すると自動tailscale upを無効化する
    CSCTL_AUTO_DOWN 0を指定すると自動tailscale downを無効化する
    CSCTL_CODE_SERVER_BIN code-serverのパスを明示する
    CSCTL_TAILSCALE_BIN tailscaleのパスを明示する

Tailscaleの自動接続について:

  • Tailscaleを常駐させる必要はありません。csctl startが未接続(Stopped)を検出すると自動でtailscale upし、 最後のインスタンスを停止した時点で自動的にtailscale downします
  • 自動切断はcsctlが接続した場合のみ行われます。自分でtailscale upした接続はcsctlが切ることはありません
  • サインインが必要な状態(NeedsLogin)ではブラウザ操作が要るため自動接続しません。手動でtailscale upしてください

補足:

  • 証明書は起動のたびに有効期限をチェックし、残り30日を切っていれば自動で更新されます
  • 拡張機能とsettings.jsonは全インスタンスで共有されますが、開いているタブやウィンドウ状態などの ユーザーデータはインスタンスごとに分離されます(VS Codeは同じuser-data-dirを複数のサーバーで同時に使えないため)
  • Macがスリープするとインスタンスも応答しなくなります。常時アクセスしたい場合はcaffeinateなどで抑止してください
  • Macを再起動するとインスタンスは終了します(自動起動はしないため、再度csctl startが必要)

システム設定

  • iCloud

    各種必要に応じて設定

  • アクセシビリティ > ディスプレイ

    ポインタの枠線のカラー > 白 ポインタの塗りつぶしカラー > オレンジ

  • キーボード > キーボードショートカット…

    スクリーンショット以外のショートカットをすべて削除
    「F1、F2などのキーを標準のファンクションキーとして利用」はオン

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Contributors

Languages