[{"data":1,"prerenderedAt":558},["ShallowReactive",2],{"post-index":3,"post-dotfiles-operation-and-nix":453},{"posts":4,"categories":402,"tags":411},[5,17,35,43,60,74,87,105,122,144,156,167,179,193,208,223,239,250,264,275,285,296,308,319,331,343,356,367,377,387],{"slug":6,"title":7,"description":8,"publishedAt":9,"updatedAt":9,"category":10,"tags":13,"thumbnail":16},"ai-cognitive-debt-approval"," 認知負債は「承認」で生まれる ── コーディングエージェント時代の意図負債と、個人・チームの向き合い方","コーディングエージェントに実装を任せると、コードは速く書けるのに人の理解が追いつかない。認知負債・意図負債・理解負債の違いを「下り」と「上り」で整理し、負債が生まれる承認の瞬間と、個人とチームでできる対策、まだ答えのない問いを、自分の体験から考える。","2026-11-03T17:12:07.686Z",{"name":11,"slug":12},"AI","ai",[14],{"name":15,"slug":15},"organization",null,{"slug":18,"title":19,"description":20,"publishedAt":21,"updatedAt":21,"category":22,"tags":23,"thumbnail":16},"design-harness-personal-product","デザインハーネスを個人開発で組んだ — AI が作る画面のずれを止めるまで","デザイナーのいない個人開発で、AI が作る画面のずれを止めるまでの記録。Codex の Product Design で画像の案を出して画面を作り直し、デザインファイルとコードの食い違いを正本の分担で片付け、lint・見た目の比較・AI の評価役からなるデザインハーネスで外れたら止める仕組みを組んだ。","2026-11-03T15:39:02.665Z",{"name":11,"slug":12},[24,27,30,32],{"name":25,"slug":26},"claude code","claude-code",{"name":28,"slug":29},"design system","design-system",{"name":31,"slug":31},"codex",{"name":33,"slug":34},"design harnes","design-harnes",{"slug":36,"title":37,"description":38,"publishedAt":39,"updatedAt":39,"category":40,"tags":41,"thumbnail":16},"claude-code-mods-intro","Claude Codeの「Claude Mods」とは？ 入れてみた3つのmodと、安全に入れる手順","Claude Codeの新機能Claude Mods（v2.1.287〜）を紹介する。hooks・skills・MCPとの違い、入れてみたmod 3つ（prompt-rail・md-prompt・qa-guide）の使い方、claude plugin validateで中身を確かめて入れる手順をまとめた。","2026-11-03T15:30:44.678Z",{"name":11,"slug":12},[42],{"name":25,"slug":26},{"slug":44,"title":45,"description":46,"publishedAt":47,"updatedAt":47,"category":48,"tags":51,"thumbnail":16},"worktree-shared-reverse-proxy","AI 時代の並列開発の環境構築 — git worktree ごとにアプリを同時に動かす","git worktree の並列開発で、各 worktree のフロントとバックを同時に動かす Docker Compose 構成を紹介する。ポートはずらさず共有のリバースプロキシでホスト名に振り分け、DB は 1 つを共有ネットワークに置いて全 worktree から参照する。","2026-11-03T15:24:32.067Z",{"name":49,"slug":50},"Dev Env","dev-env",[52,54,57],{"name":53,"slug":53},"git",{"name":55,"slug":56},"Docker","docker",{"name":58,"slug":59},"開発環境","development-environment",{"slug":61,"title":62,"description":63,"publishedAt":64,"updatedAt":64,"category":65,"tags":66,"thumbnail":16},"ghostty-herdr-dev-env","Ghostty + herdr で組む開発環境（2026年9月版） — AI エージェントを並べてターミナルで開発する","WezTerm + tmux + 自作ツールから Ghostty + herdr に移した私の開発環境を紹介する。Claude Code・Codex・takt を並べて動かす 1 タスクの流れ、ターミナル内ブラウザで画面の要素を AI に渡す方法、Neovim・lazygit・hunk・Nix まで層ごとにまとめた。","2026-11-03T15:14:47.700Z",{"name":49,"slug":50},[67,68,70,72],{"name":58,"slug":59},{"name":69,"slug":69},"herdr",{"name":71,"slug":71},"ghostty",{"name":73,"slug":73},"terminal",{"slug":75,"title":76,"description":77,"publishedAt":78,"updatedAt":78,"category":79,"tags":80,"thumbnail":16},"dotfiles-operation-and-nix","設定ファイルを端末に置いたままにしない — dotfiles 運用のすすめと、その先の Nix","設定ファイルを Git リポジトリで管理する dotfiles 運用の始め方と、その先で効いてくる Nix の話。何を入れて何を入れないか、配線の 3 手法の比較、運用して見えてくる穴、3 端末を 1 つの設定で回すところまでを実例で書く。","2026-11-03T15:04:48.084Z",{"name":49,"slug":50},[81,82,85],{"name":58,"slug":59},{"name":83,"slug":84},"Nix","nix",{"name":86,"slug":86},"dotfiles",{"slug":88,"title":89,"description":90,"publishedAt":91,"updatedAt":91,"category":92,"tags":93,"thumbnail":102},"opencode-go-plan-evaluation","OpenCode Go プランを評価する — 月$10 で LLM オーケストレーションの中間層は作れるか","OpenCode が提供する月額固定の Go プラン（初月$5、翌月以降$10\u002F月）を、料金体系・想定ユースケース・3層 LLM 構成における中間モデル層としての適合性まで含めて評価する。","2026-05-07T12:12:31.936Z",{"name":11,"slug":12},[94,97,100],{"name":95,"slug":96},"Harness Engineering","harness-engineering",{"name":98,"slug":99},"LLM","llm",{"name":101,"slug":101},"opencode",{"url":103,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F6Nh8tVuSCuHunaVeqyegdU\u002F0fee3536b9ee406b986430ebc1b8ebfd\u002FOpenCode_Go_%C3%A3__%C3%A3__%C3%A3__%C3%A3__%C3%A3__%C3%A8__%C3%A4__%C3%A3__%C3%A3__.png","",{"slug":106,"title":107,"description":108,"publishedAt":109,"updatedAt":109,"category":110,"tags":111,"thumbnail":120},"prompt-context-harness-engineering","ハーネスエンジニアリングとは何か：プロンプト→コンテキスト→ハーネスへ至るAIエージェント設計の変遷","「ハーネスエンジニアリング」とは何か。プロンプトエンジニアリング、コンテキストエンジニアリングから続く3段階の変遷を、論文引用（Lost in the Middle \u002F SWE-bench）とClaude Codeの実装例（フック・ルール・Skill）で階層構造として整理する。","2026-04-07T13:04:49.564Z",{"name":11,"slug":12},[112,115,116,119],{"name":113,"slug":114},"Prompt Engineering","prompt-engineering",{"name":95,"slug":96},{"name":117,"slug":118},"Context Engineering","context-engineering",{"name":98,"slug":99},{"url":121,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F74HnVGS2R8wphJ1nGHgLfb\u002Fb87d3d6e35ccc518e04d9959c7637a88\u002Fã__ã__ã__ã__ã__ã__ã__ã__ã__ã__ã_ªã__ã__ã__ã__ã__ä__ã__ï__ã__ã__ã__ã__ã__ã__ã__â__ã__ã__ã__ã__ã__ã__â__ã__ã__ã__ã__ã__è__ã__A.png",{"slug":123,"title":124,"description":125,"publishedAt":126,"updatedAt":126,"category":127,"tags":130,"thumbnail":142},"authjs-v5-jwt-dual-timeout-bff-django","Auth.js v5 でJWT二重タイムアウトを実装する：BFF + Django連携の設計パターン","Auth.js v5のjwt callbackで8時間の絶対有効期限と30分のアイドルタイムアウトを両立する実装方法を解説。Next.js MiddlewareによるBFF API保護とDjango REST Frameworkとの Token Exchange パターンも紹介する。","2026-04-06T12:45:28.328Z",{"name":128,"slug":129},"Programming","programming",[131,134,136,139],{"name":132,"slug":133},"auth.js","auth-js",{"name":135,"slug":135},"jwt",{"name":137,"slug":138},"Django REST Framework","django-rest-framework",{"name":140,"slug":141},"Next.js","next-js",{"url":143,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002FpqkFfHKpf2UqoNrB6KrKS\u002F672a78076f28d88b31509c230f9c3f64\u002FAuth.js_v5_ã__ã__JWTäº_é__ã__ã__ã__ã__ã__ã__ã__å__è__ã__ã__ï__BFF___Djangoé__æ_ºã__è__è__ã__ã__ã__ã__ã__.png",{"slug":145,"title":146,"description":147,"publishedAt":148,"updatedAt":148,"category":149,"tags":150,"thumbnail":154},"claude-code-spec-to-growing-claudemd","仕様書を「渡す」からCLAUDE.mdを「育てる」へ — Claude Codeの使い方が変わった話","Claude Codeに仕様書を丸ごと渡す運用から、CLAUDE.mdを継続的に育てる運用へ。「間違えたら追加」ルールの実践例と、20行→146行+31スキルに育てた記録。","2026-04-02T14:58:11.375Z",{"name":11,"slug":12},[151,152,153],{"name":25,"slug":26},{"name":113,"slug":114},{"name":117,"slug":118},{"url":155,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002FqybhQ2TQSYQhIvDhWxqvs\u002F9f8b888e11a76223c8a1b1da42df5ce1\u002Fä__æ__æ__ã__ã__æ__ã__ã__ã__ã__CLAUDE.mdã__ã__è__ã__ã__ã__ã___â___Claude_Codeã__ä__ã__æ__ã__ã__å__ã__ã__ã__è__.png",{"slug":157,"title":158,"description":159,"publishedAt":160,"updatedAt":160,"category":161,"tags":162,"thumbnail":165},"claude-code-harness-engineering","Claude Codeを覚醒させる「ハーネスエンジニアリング」 CLAUDE.md・Hooks・Rules設定の徹底解説","プロンプトを書く時代から、AIが最高に働く環境を設計する時代へ。Claude CodeのCLAUDE.md、Hooks、Rules、Settings、Agent Routingを体系的に解説し、自作のオーケストレーションシステム「ai-orchestra」による統合管理の実践例を紹介する。","2026-03-22T08:59:48.575Z",{"name":11,"slug":12},[163,164],{"name":25,"slug":26},{"name":95,"slug":96},{"url":166,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F3liirUgzBlFBuAMoytyN5E\u002F66ba6b07385791bcaae4ddf404f06ce1\u002FClaude_Codeã__è__é__ã__ã__ã__ã__ã__ã__ã__ã__ã__ã__ã__ã__ã__ã__ã_ªã__ã__ã__ã___CLAUDE.mdã__Hooksã__Rulesè__å__ã__å__åº_è__èª_.png",{"slug":168,"title":169,"description":170,"publishedAt":171,"updatedAt":172,"category":173,"tags":174,"thumbnail":177},"claude-code-dev-toolchain","Claude Code開発を支える私のツールチェイン — ターミナルからAIデザインまで","Claude Code CLIを中心とした開発環境を紹介する。WezTerm + Tmuxのターミナル構成、GTRやBatonによる並列開発、ステータスライン・通知フックのカスタマイズ、MCP\u002Fプラグイン活用、Aqua VoiceやPencilとの連携まで、実際に使っているツールチェインの全体像をまとめた。","2026-03-21T19:30:43.616Z","2026-11-03T15:18:14.982Z",{"name":49,"slug":50},[175,176],{"name":25,"slug":26},{"name":58,"slug":59},{"url":178,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F4wFUBAqC9yuIgeo3xWVHJM\u002F8843248b854fee93872b5ac519d16bc2\u002FClaude_Codeé__ç_ºã__æ__ã__ã__ç__ã__ã__ã__ã__ã__ã__ã__ã___â___ã__ã__ã__ã__ã__ã__ã__AIã__ã__ã_µã__ã__ã__ã__ã__ã__.png",{"slug":180,"title":181,"description":182,"publishedAt":183,"updatedAt":184,"category":185,"tags":186,"thumbnail":191},"lazygit-commit-message-auto-generate","【コピペで導入】lazygit × Claude Code でコミットメッセージ自動生成","lazygit の customCommands と Claude Code の --print モードを組み合わせ、Ctrl+G 一発で Conventional Commits 形式のコミットメッセージを自動生成する方法を解説。コピペ可能な完成形設定と、4段階の試行錯誤で得たハマりポイントを共有します。","2026-03-15T04:36:20.532Z","2026-11-03T15:18:53.496Z",{"name":49,"slug":50},[187,188,189],{"name":53,"slug":53},{"name":25,"slug":26},{"name":190,"slug":190},"lazygit",{"url":192,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F62ov1mzeLUjRm6cbzQ7R92\u002F3bc21a7fe7ec517ad3258b6264d4097e\u002Fã__ã__ã__ã__ã__ã__ã__ã__å__å__ã__lazygit_Ã__Claude_Code_ã__ã__ã__ã__ã__ã__ã__ã__ã__ã__ã__ã__è_ªå__ç__æ__.png",{"slug":194,"title":195,"description":196,"publishedAt":197,"updatedAt":198,"category":199,"tags":200,"thumbnail":206},"git-worktree-runner-guide","git-worktree-runner で worktree 管理が劇的にラクになった話","git-worktree-runner（GTR）は git worktree の操作を 1 コマンドに集約する CLI ラッパーです。Taskfile.yaml での運用から GTR に移行した体験を元に、インストール・設定・AI 並列開発での活用法を解説します。","2026-03-09T15:32:31.316Z","2026-11-03T15:18:33.924Z",{"name":49,"slug":50},[201,202,204],{"name":53,"slug":53},{"name":203,"slug":203},"git-worktree",{"name":205,"slug":205},"git-worktree-runner",{"url":207,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F7BRBfHFocF2ABOeOQmtIic\u002Fa63ce99493098bc42123e4315f43d768\u002Fgit-worktree-runner_ã__ã___worktree_ç__ç__ã__ã__å__ç__ã__ã__ã__ã__ã_ªã__ã__è__.png",{"slug":209,"title":210,"description":211,"publishedAt":212,"updatedAt":212,"category":213,"tags":214,"thumbnail":221},"rag-basic-knowledge","RAG入門 — 検索拡張生成の基礎知識を実験結果とともに学ぶ","RAG（検索拡張生成）の仕組みをVectorDB・Embedding・チャンク分割の3要素に分解して解説。Embeddingモデル変更で正答率0\u002F5→4\u002F5に改善した検証データも公開。","2026-03-01T11:29:25.841Z",{"name":11,"slug":12},[215,218,219],{"name":216,"slug":217},"RAG","rag",{"name":98,"slug":99},{"name":220,"slug":220},"ollama",{"url":222,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F1enfpUixTuvis0oX9x5Cfa\u002F934aed76ac994412ceb37dfdf5dc6f6c\u002FRAGå__é___â___æ__ç__æ__å_µç__æ__ã__å_ºç__ç__è__ã__å__é__çµ_æ__ã__ã__ã__ã__å__ã_µã__.png",{"slug":224,"title":225,"description":226,"publishedAt":227,"updatedAt":228,"category":229,"tags":230,"thumbnail":237},"engineer-ai-daily-workflow","1人のエンジニアがAIを日常にどう組み込んでいるか — 契約サービスから活用フローまで全公開","Claude Code、ChatGPT、Gemini、Aqua Voice、Adobe Fireflyの5サービスを使い分けるエンジニアが、開発・情報収集・ブレスト・音声入力・振り返りまで、AIを日常に組み込んだリアルな活用フローを全公開します。","2026-02-18T14:13:58.531Z","2026-02-18T14:14:12.514Z",{"name":11,"slug":12},[231,232,234],{"name":98,"slug":99},{"name":233,"slug":233},"openclaw",{"name":235,"slug":236},"マルチエージェント","multi-agent",{"url":238,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F5gBC06O8WX2GX8wmoOXQfy\u002F27a11e0f280f642a781e462dead9d3ae\u002F1äººã__ã__ã__ã__ã__ã__ã__ã__ã__AIã__æ__å__ã__ã__ã__ã__çµ_ã__è__ã__ã__ã__ã__ã__ã___â___å__ç__ã_µã__ã__ã__ã__ã__ã__æ__ç__ã__ã_.png",{"slug":240,"title":241,"description":242,"publishedAt":243,"updatedAt":243,"category":244,"tags":245,"thumbnail":248},"ai-orchestra-design-and-practice","Claude Codeマルチエージェント実践｜AI Orchestraで超えた2つの壁","Claude Code単体運用で生じるコンテキスト溢れ・役割混乱の課題を、25種の専門エージェントとClaude×Codex×Geminiのマルチモデル連携で解決するオーケストレーションシステム「AI Orchestra」。その設計思想、Hookによる自動ルーティング、並列レビューなどの実装を具体的に紹介します。","2026-02-08T08:01:16.212Z",{"name":11,"slug":12},[246,247],{"name":235,"slug":236},{"name":98,"slug":99},{"url":249,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F6j2KWOOo7wJfUobTCg3o2w\u002F126c8d413cd9a56e64fba107544ec4e1\u002FClaude_Codeã__ã__ã__ã__ã__ã__ã__ã__ã__ã__å__è_µï__AI_Orchestraã__ã__è__ã__ã__2ã__ã__å__.png",{"slug":251,"title":252,"description":253,"publishedAt":254,"updatedAt":255,"category":256,"tags":257,"thumbnail":262},"ai-benchmark-guide","AIベンチマーク読み方ガイド｜SWE-bench・GPQA・ARC-AGIの意味と活用法","SWE-bench、GPQA Diamond、ARC-AGI、Terminal-Benchなど主要AIベンチマークの読み方を解説。2026年2月最新のモデルスコア比較と、コーディングエージェントへのタスク振り分けに活かす実践的な指針を紹介します。","2026-02-07T16:10:19.131Z","2026-02-07T16:11:56.095Z",{"name":11,"slug":12},[258,259],{"name":98,"slug":99},{"name":260,"slug":261},"Benchmark","benchmark",{"url":263,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F3QlfPsXmLogdj9o4mdgFYQ\u002F73a69ca272ac4e4fdcf977f322c0218a\u002FAIã__ã__ã__ã__ã__ã__ã__èª_ã__æ__ã__ã__ã__ã__ã__ï__SWE-benchã__GPQAã__ARC-AGIã__æ__å__ã__æ__ç__æ__.png",{"slug":265,"title":266,"description":267,"publishedAt":268,"updatedAt":268,"category":269,"tags":270,"thumbnail":273},"multi-agent-building-guide","マルチエージェント構築入門｜構成の全体像と3つの実装アプローチ","マルチエージェントの構築方法を解説。オーケストレーター・サブエージェント・ツール連携の3要素から、自作・フレームワーク・既存ツール活用の選択肢まで。Claude Codeでの実装例付き。","2026-02-05T12:52:58.095Z",{"name":11,"slug":12},[271,272],{"name":98,"slug":99},{"name":235,"slug":236},{"url":274,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F68XZFvLA17pegNy0lHCrYN\u002Ff90202a483bf087f4fa4cbb80dae934e\u002Fã__ã__ã__ã__ã__ã__ã__ã__ã__ã__æ__ç__å__é__ï__æ__æ__ã__å__ä__å__ã__3ã__ã__å__è__ã__ã__ã__ã__ã__ã__.png",{"slug":276,"title":277,"description":278,"publishedAt":279,"updatedAt":279,"category":280,"tags":281,"thumbnail":283},"openclaw-security-and-features","OpenClaw徹底解説 ── セルフホスト型AIアシスタントの可能性とセキュリティリスク","「AIが自分の代わりにメッセージを返信してくれたら…」そんな願いを叶えるツールが、いま爆発的な人気を集めています。その名も「OpenClaw」。わずか2ヶ月でGitHub Stars 10万を突破し、週間200万人が訪れるほどの注目を浴びています。  しかし、その急成長の裏には商標問題、詐欺騒動、そして深刻なセキュリティ脆弱性の発覚と、波乱万丈なドラマがありました。","2026-02-03T15:20:26.415Z",{"name":11,"slug":12},[282],{"name":233,"slug":233},{"url":284,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F7aKouVOZSGnzVUGgLQfa22\u002F5688eb515dc5f0b2c301b9b8c267ec20\u002F00036.png",{"slug":286,"title":287,"description":104,"publishedAt":288,"updatedAt":289,"category":290,"tags":291,"thumbnail":294},"custom-instructions","ChatGPTやClaudeがもっと賢くなる！カスタム指示の書き方と実例集","2026-01-30T17:42:57.415Z","2026-02-03T15:03:06.461Z",{"name":11,"slug":12},[292,293],{"name":98,"slug":99},{"name":113,"slug":114},{"url":295,"description":287},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F4EojonTOebJGeiaXf98ol0\u002F9e391fc8bd6e548f8c96136c805b49d7\u002FChatGPTã__Claudeã__ã__ã__ã__ã__è__ã__ã_ªã__ï__ã__ã__ã__ã__æ__ç_ºã__æ__ã__æ__ã__å__ä__é__.png",{"slug":297,"title":298,"description":299,"publishedAt":300,"updatedAt":301,"category":302,"tags":303,"thumbnail":306},"ollama-local-llm-setup","ローカルLLMはまず小さめモデルから：Ollamaで始める導入〜疎通確認","acでOllamaを使い、ローカルLLMを最短で起動→1回応答まで確認する手順を解説。モデル選定の考え方、遅い\u002Fメモリ\u002Fディスクの切り分け、API疎通(curl)もまとめます。","2026-01-18T06:40:49.270Z","2026-01-21T13:05:14.924Z",{"name":11,"slug":12},[304,305],{"name":220,"slug":220},{"name":98,"slug":99},{"url":307,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F1NQmtspOD1S6LEb4SrCYIQ\u002F49c213de131a9a4ea139c32e805f3b01\u002Fã__ã__ã__ã__LLMã__ã__ã__ã__å__ã__ã__ã__ã__ã__ã__ã__ã__ï__Ollamaã__ã__å__ã__ã__å__å__ã__ç__é__ç_ºèª_.png",{"slug":309,"title":310,"description":104,"publishedAt":311,"updatedAt":312,"category":313,"tags":314,"thumbnail":317},"llm-leaderboard-score-guide","LLMリーダーボードの読み方：スコア指針とベンチマーク確認手順","2026-01-10T08:57:26.636Z","2026-01-18T06:55:58.990Z",{"name":11,"slug":12},[315,316],{"name":260,"slug":261},{"name":98,"slug":99},{"url":318,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F8u7GQ3mAlcvIHOc5bFJc6\u002Fa953bf74c33117efa393eaf8541f515a\u002FLLMã_ªã__ã__ã__ã__ã__ã__ã__ã__ã__ã__èª_ã__æ__.png",{"slug":320,"title":321,"description":104,"publishedAt":322,"updatedAt":322,"category":323,"tags":324,"thumbnail":329},"vite_docker_esbuild-error","Vite + Docker + ESBuild Errorの解決","2021-11-12T03:53:22.965Z",{"name":128,"slug":129},[325,326],{"name":55,"slug":56},{"name":327,"slug":328},"Vite","vite",{"url":330,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F5gqsl4ZChCI3uaSXQA86du\u002F3c94f9928ffaaab2d8911d32e742de1e\u002Fprogramming.jpg",{"slug":332,"title":333,"description":104,"publishedAt":334,"updatedAt":335,"category":336,"tags":337,"thumbnail":341},"nuxt-router-scroll-control","Nuxt.jsのページ遷移時のスクロール制御したい","2021-10-28T14:31:11.276Z","2021-10-28T14:35:33.155Z",{"name":128,"slug":129},[338],{"name":339,"slug":340},"Nuxt.js","nuxt-js",{"url":342,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F6dqpNPZsBJJ7x9XJj1Ld1z\u002Fcd1bb9323a8ffb0657ad79525403e1ff\u002Fnuxt.jpg",{"slug":344,"title":345,"description":104,"publishedAt":346,"updatedAt":347,"category":348,"tags":349,"thumbnail":354},"react-drf-img_uploader-backend","React × DRFで画像アップロード機能作成 (バックエンド実装編)","2021-10-26T02:56:58.215Z","2021-10-27T11:37:43.829Z",{"name":128,"slug":129},[350,353],{"name":351,"slug":352},"React","react",{"name":137,"slug":138},{"url":355,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F4jbVtK0nXsMKZplpBAwhri\u002Fb0ccef5153d92a7947eb0c682008255c\u002Fdjango-rest-framework.jpg",{"slug":357,"title":358,"description":104,"publishedAt":359,"updatedAt":360,"category":361,"tags":362,"thumbnail":365},"react-drf-img_uploader-frontend","React × DRFで画像アップロード機能作成 (フロント実装編)","2021-10-26T02:39:01.629Z","2021-10-27T11:37:24.670Z",{"name":128,"slug":129},[363,364],{"name":351,"slug":352},{"name":137,"slug":138},{"url":366,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F3uFOs5NyHOvRqYgE1lQ3p6\u002Fd79c8fabb7d4151c5773ffe1cdd2bce3\u002Freact.jpg",{"slug":368,"title":369,"description":104,"publishedAt":370,"updatedAt":371,"category":372,"tags":373,"thumbnail":376},"react-drf-img_uploader-environment","React × DRFで画像アップロード機能作成 (環境構築編)","2021-10-26T02:00:01.702Z","2021-10-27T11:47:28.398Z",{"name":128,"slug":129},[374,375],{"name":351,"slug":352},{"name":137,"slug":138},{"url":330,"description":104},{"slug":378,"title":379,"description":104,"publishedAt":380,"updatedAt":381,"category":382,"tags":383,"thumbnail":385},"docker-install-procedure","各OS毎のDockerインストール手順","2021-10-26T01:43:54.692Z","2022-02-07T14:32:07.791Z",{"name":128,"slug":129},[384],{"name":55,"slug":56},{"url":386,"description":104},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F3aVlE8NIfX12QvTafaC8xE\u002Fd00bf480e212538e1816fc10d2e7b77d\u002Fdocker.jpg",{"slug":388,"title":389,"description":104,"publishedAt":390,"updatedAt":391,"category":392,"tags":395,"thumbnail":399},"notion-clear-trash","Notionのゴミ箱を一括削除する","2021-09-12T13:04:59.853Z","2021-10-21T14:29:03.528Z",{"name":393,"slug":394},"Lifestyle","lifestyle",[396],{"name":397,"slug":398},"Notion","notion",{"url":400,"description":401},"https:\u002F\u002Fimages.ctfassets.net\u002Fd3b1looh8iwg\u002F5binM4YxsZdSXX9SKkGyNP\u002Fcf39353211db8b3e0c4ac18e03b55527\u002Fnotion.jpg","Notion記事用のサムネイル",[403,405,407,409],{"name":49,"slug":50,"count":404},6,{"name":11,"slug":12,"count":406},16,{"name":128,"slug":129,"count":408},7,{"name":393,"slug":394,"count":410},1,[412,414,415,417,418,420,421,422,423,424,425,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450],{"name":98,"slug":99,"count":413},10,{"name":25,"slug":26,"count":404},{"name":137,"slug":138,"count":416},4,{"name":58,"slug":59,"count":416},{"name":55,"slug":56,"count":419},3,{"name":53,"slug":53,"count":419},{"name":95,"slug":96,"count":419},{"name":113,"slug":114,"count":419},{"name":351,"slug":352,"count":419},{"name":235,"slug":236,"count":419},{"name":260,"slug":261,"count":426},2,{"name":117,"slug":118,"count":426},{"name":220,"slug":220,"count":426},{"name":233,"slug":233,"count":426},{"name":132,"slug":133,"count":410},{"name":31,"slug":31,"count":410},{"name":33,"slug":34,"count":410},{"name":28,"slug":29,"count":410},{"name":86,"slug":86,"count":410},{"name":71,"slug":71,"count":410},{"name":203,"slug":203,"count":410},{"name":205,"slug":205,"count":410},{"name":69,"slug":69,"count":410},{"name":135,"slug":135,"count":410},{"name":190,"slug":190,"count":410},{"name":140,"slug":141,"count":410},{"name":83,"slug":84,"count":410},{"name":397,"slug":398,"count":410},{"name":339,"slug":340,"count":410},{"name":101,"slug":101,"count":410},{"name":15,"slug":15,"count":410},{"name":216,"slug":217,"count":410},{"name":73,"slug":73,"count":410},{"name":327,"slug":328,"count":410},{"name":451,"slug":451,"count":452},"takt",0,{"slug":75,"title":76,"description":77,"publishedAt":78,"updatedAt":78,"category":454,"tags":455,"thumbnail":16,"html":459,"headings":460,"readingMinutes":523,"older":524,"newer":531,"related":538},{"name":49,"slug":50},[456,457,458],{"name":58,"slug":59},{"name":83,"slug":84},{"name":86,"slug":86},"\u003Ch1 id=\"設定ファイルを端末に置いたままにしない\" tabindex=\"-1\">設定ファイルを端末に置いたままにしない\u003C\u002Fh1>\n\u003Ch2 id=\"結論から\" tabindex=\"-1\">\u003Cspan class=\"h-no\" aria-hidden=\"true\">01\u003C\u002Fspan>結論から\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>開発環境の設定ファイルを、端末に置いたままにしない。\u003C\u002Fstrong> Git リポジトリに移して、端末側にはリンクを張るだけにする。dotfiles 運用と呼ばれているもので、やることは実質これだけだ。\u003C\u002Fp>\n\u003Cp>見返りは分かりやすい。新しい端末で環境を作り直さなくて済む。clone してリンクを張れば元に戻る。設定をいじって壊しても、どこをどう変えたかが \u003Ccode>git diff\u003C\u002Fcode> で追える。2 台目以降で「あっちにはあるのにこっちには無い」が消える。\u003C\u002Fp>\n\u003Cp>ただ、運用が回り始めると穴が見えてくる。設定ファイルは揃うのに、そのファイルを読むツール本体は揃わない。ここを埋めるのが Nix だ。\u003C\u002Fp>\n\u003Cp>この記事は前半で dotfiles 運用の始め方を、後半で「その先に Nix を足すとどうなるか」を書く。私は現在 3 台（MacBook Pro \u002F Mac mini \u002F 会社支給 Windows の WSL2）をこの方式で回していて、\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fyoshihiko555\u002Fdotfiles\" target=\"_blank\" rel=\"noopener noreferrer\">リポジトリは公開している\u003C\u002Fa>。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"dotfiles-とは何か\" tabindex=\"-1\">\u003Cspan class=\"h-no\" aria-hidden=\"true\">02\u003C\u002Fspan>dotfiles とは何か\u003C\u002Fh2>\n\u003Cp>Unix 系 OS では、設定ファイルの名前が \u003Ccode>.\u003C\u002Fcode> で始まる慣習がある。\u003Ccode>.zshrc\u003C\u002Fcode>、\u003Ccode>.gitconfig\u003C\u002Fcode>、\u003Ccode>.vimrc\u003C\u002Fcode> のたぐいだ。これらをまとめて \u003Cstrong>dotfiles\u003C\u002Fstrong> と呼ぶ。\u003C\u002Fp>\n\u003Cp>最近は \u003Ccode>~\u002F.config\u002F\u003C\u002Fcode> 以下に置く形式（XDG Base Directory）が主流になっていて、\u003Ccode>.\u003C\u002Fcode> で始まらないものも増えた。私のリポジトリで管理している中身はこのあたりになる。\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>シェル\u003C\u002Fstrong>: \u003Ccode>.zshenv\u003C\u002Fcode> \u002F \u003Ccode>.zprofile\u003C\u002Fcode> \u002F \u003Ccode>.zshrc\u003C\u002Fcode> と、分割した \u003Ccode>zsh\u002F\u003C\u002Fcode> 配下のスクリプト群\u003C\u002Fli>\n\u003Cli>\u003Cstrong>エディタ\u003C\u002Fstrong>: \u003Ccode>nvim\u002F\u003C\u002Fcode>（Lua の設定一式）\u003C\u002Fli>\n\u003Cli>\u003Cstrong>ターミナル\u003C\u002Fstrong>: \u003Ccode>wezterm\u002F\u003C\u002Fcode> \u003Ccode>ghostty\u002F\u003C\u002Fcode> \u003Ccode>tmux\u002F\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Git\u003C\u002Fstrong>: \u003Ccode>git\u002F\u003C\u002Fcode>（\u003Ccode>.gitconfig\u003C\u002Fcode> と \u003Ccode>.gitignore_global\u003C\u002Fcode>）\u003C\u002Fli>\n\u003Cli>\u003Cstrong>プロンプト\u003C\u002Fstrong>: \u003Ccode>starship\u002F\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Cstrong>その他\u003C\u002Fstrong>: \u003Ccode>mise\u002F\u003C\u002Fcode>（言語ランタイム管理）、\u003Ccode>lazygit\u002F\u003C\u002Fcode>、\u003Ccode>karabiner\u002F\u003C\u002Fcode>（キー配列）、\u003Ccode>aerospace\u002F\u003C\u002Fcode>（ウィンドウ管理）\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>新しい端末に移ったときに無いと困るもの。それが全部対象になる。\u003C\u002Fp>\n\u003Ch3 id=\"端末に置いたままだと何が起きるか\" tabindex=\"-1\">端末に置いたままだと何が起きるか\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>セットアップが記憶頼りになる。\u003C\u002Fstrong> 前の端末で何を入れて、どう設定したかを思い出しながらやる。移行前の私は「何をどの順で入れるか」が完全に頭の中にしか無かった。しかも順序には依存関係がある。Homebrew の前に Xcode Command Line Tools が要る、みたいなやつだ。それがどこにも書かれていない。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>端末間でドリフトする。\u003C\u002Fstrong> 2 台目で \u003Ccode>.zshrc\u003C\u002Fcode> にエイリアスを 1 つ足す。1 台目には無い。3 か月後、1 台目で同じエイリアスを打って「あれ、効かない」となる。台数が増えるほど確実に起きる。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>壊したときに戻せない。\u003C\u002Fstrong> 設定をいじって動かなくなったとき、どこを触ったか分からない。\u003Ccode>.zshrc.bak\u003C\u002Fcode> \u003Ccode>.zshrc.bak2\u003C\u002Fcode> \u003Ccode>.zshrc.old\u003C\u002Fcode> が生えていく。私はこれを何度もやった。\u003C\u002Fp>\n\u003Cp>全部「バージョン管理されていないから」起きている。だから Git に載せる。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"何を入れて、何を入れないか\" tabindex=\"-1\">\u003Cspan class=\"h-no\" aria-hidden=\"true\">03\u003C\u002Fspan>何を入れて、何を入れないか\u003C\u002Fh2>\n\u003Ch3 id=\"入れるもの\" tabindex=\"-1\">入れるもの\u003C\u002Fh3>\n\u003Cp>判断基準はシンプルで、**「新しい端末で手で書き直すのが面倒なもの」**は全部入れる。\u003C\u002Fp>\n\u003Cp>私のリポジトリはこういう構成になっている。\u003C\u002Fp>\n\u003Cfigure class=\"code\" data-lang=\"text\">\u003Cdiv class=\"code-bar\">\u003Cspan class=\"lang\">TEXT\u003C\u002Fspan>\u003Cspan>12 LINES\u003C\u002Fspan>\u003Cbutton class=\"copy\" type=\"button\" data-cursor=\"COPY\">COPY\u003C\u002Fbutton>\u003C\u002Fdiv>\u003Cpre class=\"shiki site-code\" style=\"background-color:var(--shiki-background);color:var(--shiki-foreground)\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>dotfiles\u002F\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>├── shell\u002F        # zsh 設定（zshenv \u002F zprofile \u002F zshrc + zsh\u002F 分割）\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>├── config\u002F       # ~\u002F.config へ配線するもの\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>│   ├── nvim\u002F     # エディタ\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>│   ├── git\u002F      # Git\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>│   ├── tmux\u002F     # ターミナルマルチプレクサ\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>│   ├── starship\u002F # プロンプト\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>│   └── ...       # wezterm, ghostty, mise, lazygit, karabiner, aerospace, gh\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>├── home\u002F         # $HOME 直下に置く単体ファイル（.editorconfig 等）\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>├── ssh\u002F          # ~\u002F.ssh\u002Fconfig（後述。鍵は入れない）\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>├── scripts\u002F      # セットアップ用のシェルスクリプト\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>└── README.md     # 復元手順のメモ\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Ffigure>\n\u003Cp>\u003Ccode>README.md\u003C\u002Fcode> を軽視しないでほしい。数か月後に読み返すための手順書になる。私はここに「まっさらな Mac から復元する手順」を書いていて、リポジトリの中で一番効いている資産はこれだと思っている。設定ファイルそのものより効いている。\u003C\u002Fp>\n\u003Ch3 id=\"絶対に入れてはいけないもの\" tabindex=\"-1\">絶対に入れてはいけないもの\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>秘密情報はコミットしない。\u003C\u002Fstrong> ここが dotfiles 運用で唯一、事故ると本当に痛いポイントになる。\u003C\u002Fp>\n\u003Cul>\n\u003Cli>SSH の秘密鍵（\u003Ccode>~\u002F.ssh\u002Fid_ed25519\u003C\u002Fcode> など）\u003C\u002Fli>\n\u003Cli>API トークン、アクセスキー\u003C\u002Fli>\n\u003Cli>パスワードを含む設定ファイル\u003C\u002Fli>\n\u003Cli>\u003Ccode>.env\u003C\u002Fcode> ファイル\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>公開リポジトリでなくても入れない。\u003C\u002Fstrong> private でも、後で public に切り替えたときや、コラボレータを追加したときに漏れる。一度コミットしたものは履歴に残るので、消すのも面倒だ。\u003C\u002Fp>\n\u003Cp>私は \u003Ccode>~\u002F.ssh\u002Fconfig\u003C\u002Fcode> を入れているが、これは中身が接続先のホスト名とポート番号だけだからだ。鍵そのものは入れていない。\u003C\u002Fp>\n\u003Cp>トークン類をどうしても管理下に置きたい場合は、暗号化したままコミットする仕組み（\u003Ccode>sops\u003C\u002Fcode> \u002F \u003Ccode>git-crypt\u003C\u002Fcode> \u002F \u003Ccode>age\u003C\u002Fcode> など）を使う。私は将来のために \u003Ccode>sops-nix\u003C\u002Fcode> の仕組みだけ用意してあるが、\u003Cstrong>現時点で管理対象はゼロ\u003C\u002Fstrong>にしている。必要になるまで入れない、というのも判断のうちだ。\u003C\u002Fp>\n\u003Ch3 id=\"迷うもの\" tabindex=\"-1\">迷うもの\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>\u003Cstrong>端末固有の設定\u003C\u002Fstrong>（ディスプレイ解像度依存のもの、会社 PC 専用の proxy 設定など）→ 入れてよい。ただし後述の「端末差分」の扱いを決めてから\u003C\u002Fli>\n\u003Cli>\u003Cstrong>アプリが自動生成する設定\u003C\u002Fstrong>（起動のたびに書き換わるもの）→ 入れると差分ノイズになる。\u003Ccode>.gitignore\u003C\u002Fcode> するか、必要な部分だけ切り出す\u003C\u002Fli>\n\u003Cli>\u003Cstrong>巨大なバイナリ\u003C\u002Fstrong>（フォント、プラグインのキャッシュ）→ 入れない。リポジトリが重くなるだけで、再取得できる\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Chr>\n\u003Ch2 id=\"端末へどう配線するか\" tabindex=\"-1\">\u003Cspan class=\"h-no\" aria-hidden=\"true\">04\u003C\u002Fspan>端末へどう配線するか\u003C\u002Fh2>\n\u003Cp>リポジトリを作ったら、次は \u003Ccode>~\u002F.zshrc\u003C\u002Fcode> からリポジトリの中身を参照させる作業に入る。やり方は主に 3 つで、後になるほど強力で、後になるほど重い。\u003C\u002Fp>\n\u003Ch3 id=\"方法-1:-手書きのシェルスクリプト\" tabindex=\"-1\">方法 1: 手書きのシェルスクリプト\u003C\u002Fh3>\n\u003Cp>\u003Ccode>ln -s\u003C\u002Fcode> を並べたスクリプトを書く。\u003C\u002Fp>\n\u003Cfigure class=\"code\" data-lang=\"sh\">\u003Cdiv class=\"code-bar\">\u003Cspan class=\"lang\">SH\u003C\u002Fspan>\u003Cspan>7 LINES\u003C\u002Fspan>\u003Cbutton class=\"copy\" type=\"button\" data-cursor=\"COPY\">COPY\u003C\u002Fbutton>\u003C\u002Fdiv>\u003Cpre class=\"shiki site-code\" style=\"background-color:var(--shiki-background);color:var(--shiki-foreground)\" tabindex=\"0\">\u003Ccode class=\"language-sh\">\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-token-comment)\">#!\u002Fusr\u002Fbin\u002Fenv bash\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-foreground)\">DOTFILES\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-keyword)\">=\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string-expression)\">\"$HOME\u002Fghq\u002Fgithub.com\u002Fyoshihiko555\u002Fdotfiles\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-token-function)\">ln\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> -sfn\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string-expression)\"> \"$DOTFILES\u002Fshell\u002Fzshrc\"\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string-expression)\">     \"$HOME\u002F.zshrc\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-token-function)\">ln\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> -sfn\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string-expression)\"> \"$DOTFILES\u002Fconfig\u002Fnvim\"\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string-expression)\">     \"$HOME\u002F.config\u002Fnvim\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-token-function)\">ln\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> -sfn\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string-expression)\"> \"$DOTFILES\u002Fconfig\u002Fstarship\"\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string-expression)\"> \"$HOME\u002F.config\u002Fstarship\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-token-comment)\"># ... 以下続く\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Ffigure>\n\u003Cp>\u003Cstrong>最初はこれで十分だと思う。\u003C\u002Fstrong> 依存が何もないし、何が起きているか全部読める。10 個や 20 個ならこれで回る。\u003C\u002Fp>\n\u003Cp>弱点は、消すときの処理を手で書く必要があること（配線をやめたファイルのリンクが残り続ける）と、行数が増えると管理しづらくなることだ。\u003C\u002Fp>\n\u003Ch3 id=\"方法-2:-gnu-stow\" tabindex=\"-1\">方法 2: GNU stow\u003C\u002Fh3>\n\u003Cp>\u003Ccode>stow\u003C\u002Fcode> はディレクトリ構造をそのまま \u003Ccode>~\u002F\u003C\u002Fcode> に symlink として展開するツールで、dotfiles 界隈では長く定番だった。\u003C\u002Fp>\n\u003Cfigure class=\"code\" data-lang=\"sh\">\u003Cdiv class=\"code-bar\">\u003Cspan class=\"lang\">SH\u003C\u002Fspan>\u003Cspan>2 LINES\u003C\u002Fspan>\u003Cbutton class=\"copy\" type=\"button\" data-cursor=\"COPY\">COPY\u003C\u002Fbutton>\u003C\u002Fdiv>\u003Cpre class=\"shiki site-code\" style=\"background-color:var(--shiki-background);color:var(--shiki-foreground)\" tabindex=\"0\">\u003Ccode class=\"language-sh\">\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-token-comment)\"># dotfiles\u002Fnvim\u002F.config\u002Fnvim\u002Finit.lua という構造にしておくと\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-token-function)\">stow\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> nvim\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-comment)\">   # → ~\u002F.config\u002Fnvim\u002Finit.lua へリンクが張られる\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Ffigure>\n\u003Cp>リポジトリ側を「\u003Ccode>$HOME\u003C\u002Fcode> からの相対パス」の形に整えておく必要があり、そのぶんディレクトリが 1 階層深くなる。私も以前は stow を使っていた。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>stow の限界は「張らない」を表現できないことにある。\u003C\u002Fstrong> ヘッドレスのサーバーに GUI アプリの設定を配りたくない、という状況で、stow にできるのは「そのコマンドを打たない」だけだ。配らないという意思がリポジトリのどこにも書かれない。\u003C\u002Fp>\n\u003Ch3 id=\"方法-3:-nix（home-manager）\" tabindex=\"-1\">方法 3: Nix（home-manager）\u003C\u002Fh3>\n\u003Cp>後半で詳しく書くが、配線を宣言として書く方式になる。\u003C\u002Fp>\n\u003Cfigure class=\"code\" data-lang=\"nix\">\u003Cdiv class=\"code-bar\">\u003Cspan class=\"lang\">NIX\u003C\u002Fspan>\u003Cspan>2 LINES\u003C\u002Fspan>\u003Cbutton class=\"copy\" type=\"button\" data-cursor=\"COPY\">COPY\u003C\u002Fbutton>\u003C\u002Fdiv>\u003Cpre class=\"shiki site-code\" style=\"background-color:var(--shiki-background);color:var(--shiki-foreground)\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>home.file.\".zshrc\".source = mkLink \"shell\u002Fzshrc\";\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>xdg.configFile.\"nvim\".source = mkLink \"config\u002Fnvim\";\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Ffigure>\n\u003Cp>見た目は方法 1 とあまり変わらない。違うのは、\u003Cstrong>宣言から外したものが自動で消える\u003C\u002Fstrong>ことと、\u003Cstrong>同じ仕組みでパッケージも管理できる\u003C\u002Fstrong>ことだ。\u003C\u002Fp>\n\u003Ch3 id=\"どれを選ぶか\" tabindex=\"-1\">どれを選ぶか\u003C\u002Fh3>\n\u003Cdiv class=\"tbl\" tabindex=\"0\" role=\"region\" aria-label=\"Table\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003C\u002Fth>\n\u003Cth>手書きスクリプト\u003C\u002Fth>\n\u003Cth>GNU stow\u003C\u002Fth>\n\u003Cth>Nix (home-manager)\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>導入コスト\u003C\u002Ftd>\n\u003Ctd>ほぼゼロ\u003C\u002Ftd>\n\u003Ctd>低（\u003Ccode>brew install stow\u003C\u002Fcode>）\u003C\u002Ftd>\n\u003Ctd>高（Nix の学習が要る）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>リポジトリ構造の制約\u003C\u002Ftd>\n\u003Ctd>なし\u003C\u002Ftd>\n\u003Ctd>あり（\u003Ccode>$HOME\u003C\u002Fcode> 相対の形）\u003C\u002Ftd>\n\u003Ctd>なし\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>配線をやめたときの後始末\u003C\u002Ftd>\n\u003Ctd>手で書く\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>stow -D\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>自動\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>「配らない」の表現\u003C\u002Ftd>\n\u003Ctd>不可\u003C\u002Ftd>\n\u003Ctd>不可\u003C\u002Ftd>\n\u003Ctd>可能\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>パッケージ管理\u003C\u002Ftd>\n\u003Ctd>別途\u003C\u002Ftd>\n\u003Ctd>別途\u003C\u002Ftd>\n\u003Ctd>同じ仕組みで可能\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>\u003Cstrong>まだ dotfiles を運用していないなら、方法 1 から始めてほしい。\u003C\u002Fstrong> 最初から Nix に行くと、dotfiles 運用そのものの学習と Nix の学習が同時に来て、たいてい途中で止まる。私自身、stow で 1 年以上運用してから Nix に移った。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"運用してみると見えてくる穴\" tabindex=\"-1\">\u003Cspan class=\"h-no\" aria-hidden=\"true\">05\u003C\u002Fspan>運用してみると見えてくる穴\u003C\u002Fh2>\n\u003Cp>dotfiles 運用が回り始めると、これでは解決しない問題が残っているのに気づく。私が実際にぶつかった順に書く。\u003C\u002Fp>\n\u003Ch3 id=\"設定ファイルは揃うが、ツール本体は揃わない\" tabindex=\"-1\">設定ファイルは揃うが、ツール本体は揃わない\u003C\u002Fh3>\n\u003Cp>\u003Ccode>~\u002F.config\u002Fnvim\u002Finit.lua\u003C\u002Fcode> をリンクしても、\u003Cstrong>Neovim 本体が入っていなければ意味がない。\u003C\u002Fstrong> \u003Ccode>.tmux.conf\u003C\u002Fcode> があっても tmux が無ければ動かない。\u003C\u002Fp>\n\u003Cp>つまり dotfiles リポジトリだけでは、新端末で\u003C\u002Fp>\n\u003Cfigure class=\"code\" data-lang=\"sh\">\u003Cdiv class=\"code-bar\">\u003Cspan class=\"lang\">SH\u003C\u002Fspan>\u003Cspan>1 LINE\u003C\u002Fspan>\u003Cbutton class=\"copy\" type=\"button\" data-cursor=\"COPY\">COPY\u003C\u002Fbutton>\u003C\u002Fdiv>\u003Cpre class=\"shiki site-code\" style=\"background-color:var(--shiki-background);color:var(--shiki-foreground)\" tabindex=\"0\">\u003Ccode class=\"language-sh\">\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-token-function)\">brew\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> install\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> neovim\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> tmux\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> ripgrep\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> fd\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> fzf\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> ghq\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> lazygit\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> starship\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> zoxide\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> ...\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Ffigure>\n\u003Cp>を手で打つ工程が残る。しかも「どれを入れたか」はリポジトリのどこにも書かれていない。\u003Ccode>Brewfile\u003C\u002Fcode> を置く手はあるが、今度は Brewfile と実態がずれていく（消したはずのツールが Brewfile に残る、逆もある）。\u003C\u002Fp>\n\u003Ch3 id=\"端末差分がシェルスクリプトの-if-に溜まる\" tabindex=\"-1\">端末差分がシェルスクリプトの \u003Ccode>if\u003C\u002Fcode> に溜まる\u003C\u002Fh3>\n\u003Cp>2 台目、3 台目と増えると、こういう記述が \u003Ccode>.zshrc\u003C\u002Fcode> に生えてくる。\u003C\u002Fp>\n\u003Cfigure class=\"code\" data-lang=\"sh\">\u003Cdiv class=\"code-bar\">\u003Cspan class=\"lang\">SH\u003C\u002Fspan>\u003Cspan>7 LINES\u003C\u002Fspan>\u003Cbutton class=\"copy\" type=\"button\" data-cursor=\"COPY\">COPY\u003C\u002Fbutton>\u003C\u002Fdiv>\u003Cpre class=\"shiki site-code\" style=\"background-color:var(--shiki-background);color:var(--shiki-foreground)\" tabindex=\"0\">\u003Ccode class=\"language-sh\">\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-token-keyword)\">if\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-foreground)\"> [[ \u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string-expression)\">\"$OSTYPE\"\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-keyword)\"> ==\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-foreground)\"> darwin\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-keyword)\">*\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-foreground)\"> ]]; \u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-keyword)\">then\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-token-function)\">  eval\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string-expression)\"> \"$(\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-function)\">\u002Fopt\u002Fhomebrew\u002Fbin\u002Fbrew\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> shellenv\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string-expression)\">)\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-token-keyword)\">fi\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-token-keyword)\">if\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-foreground)\"> [[ \u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string-expression)\">\"$(\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-function)\">hostname\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string-expression)\">)\"\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-keyword)\"> ==\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string-expression)\"> \"work-mac\"\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-foreground)\"> ]]; \u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-keyword)\">then\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-token-keyword)\">  export\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-foreground)\"> HTTP_PROXY\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-keyword)\">=\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string-expression)\">\"http:\u002F\u002Fproxy.example.com:8080\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-token-keyword)\">fi\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Ffigure>\n\u003Cp>1 つ 2 つなら問題ない。ただし減ることはまずない。増える一方で、しかも「この端末では何が有効なのか」を知るには全部読むしかない。\u003C\u002Fp>\n\u003Ch3 id=\"手順の順序が、結局まだ暗黙のまま\" tabindex=\"-1\">手順の順序が、結局まだ暗黙のまま\u003C\u002Fh3>\n\u003Cp>README に手順を書いても、それは実行されない文章でしかない。書き忘れれば抜ける。更新を忘れれば古くなる。手順が正しいことを保証する仕組みがどこにもない。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"プラスアルファ:-ここで-nix-を足す\" tabindex=\"-1\">\u003Cspan class=\"h-no\" aria-hidden=\"true\">06\u003C\u002Fspan>プラスアルファ: ここで Nix を足す\u003C\u002Fh2>\n\u003Cp>この 3 つは、どれも「設定ファイル以外のものも宣言したい」という同じ要求に行き着く。Nix はそこを埋める道具だ。\u003C\u002Fp>\n\u003Ch3 id=\"nix-が-dotfiles-に足すもの\" tabindex=\"-1\">Nix が dotfiles に足すもの\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>パッケージがリポジトリに書かれる。\u003C\u002Fstrong> 私は CLI を 1 ファイルに宣言している。\u003C\u002Fp>\n\u003Cfigure class=\"code\" data-lang=\"nix\">\u003Cdiv class=\"code-bar\">\u003Cspan class=\"lang\">NIX\u003C\u002Fspan>\u003Cspan>8 LINES\u003C\u002Fspan>\u003Cbutton class=\"copy\" type=\"button\" data-cursor=\"COPY\">COPY\u003C\u002Fbutton>\u003C\u002Fdiv>\u003Cpre class=\"shiki site-code\" style=\"background-color:var(--shiki-background);color:var(--shiki-foreground)\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan># home\u002Fpackages.nix — 3 台共通の CLI\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>{ pkgs, ... }:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>{\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  home.packages = with pkgs; [\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    fd fzf gh ghq git glow lazygit mise\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    neovim ripgrep starship tmux tree yazi zoxide d2\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  ];\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Ffigure>\n\u003Cp>このファイルがそのまま「この環境に入っているものの一覧」になる。バージョンは \u003Ccode>flake.lock\u003C\u002Fcode> で固定されるので、3 台が同じところに揃う。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>「入れない」が書ける。\u003C\u002Fstrong> ヘッドレスの Mac mini には GUI アプリを配らない、という意思を宣言として残せる。stow との決定的な差はここだ。私の場合、GUI アプリ（cask）は MacBook Pro に 13 個、Mac mini に 7 個で宣言が分かれている。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>端末差分が宣言に移る。\u003C\u002Fstrong> シェルスクリプトの \u003Ccode>if\u003C\u002Fcode> から、ホストごとのファイルへ移る。私は 3 層に分けた。\u003C\u002Fp>\n\u003Cfigure class=\"code\" data-lang=\"text\">\u003Cdiv class=\"code-bar\">\u003Cspan class=\"lang\">TEXT\u003C\u002Fspan>\u003Cspan>4 LINES\u003C\u002Fspan>\u003Cbutton class=\"copy\" type=\"button\" data-cursor=\"COPY\">COPY\u003C\u002Fbutton>\u003C\u002Fdiv>\u003Cpre class=\"shiki site-code\" style=\"background-color:var(--shiki-background);color:var(--shiki-foreground)\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>config\u002Fnix\u002F\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>├── darwin\u002F   # Mac 2 台共通のシステム設定（WSL2 は通らない）\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>├── home\u002F     # 3 台すべてで共有するユーザー設定\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>└── hosts\u002F    # ホスト固有（macbook \u002F hermes \u002F wsl）\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Ffigure>\n\u003Cp>ルールは 2 つだけにしている。\u003Cstrong>新規はまず、使うホストの \u003Ccode>hosts\u002F\u003C\u002Fcode> に書く。2 台以上で使い始めたら共通層へ昇格させる。\u003C\u002Fstrong> 「将来使うかもしれないから共通層へ」をやると、共通層が条件分岐の塊に戻ってしまう。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>設定ファイルは書き直さなくていい。\u003C\u002Fstrong> ここが一番言いたいところで、Nix を入れても既存の dotfiles を捨てる必要はない。\u003C\u002Fp>\n\u003Cp>home-manager には設定内容を Nix の属性として書く流儀（\u003Ccode>programs.git.userName = &quot;...&quot;\u003C\u002Fcode> のような書き方）もあるが、私はそれを採らなかった。代わりに \u003Ccode>mkOutOfStoreSymlink\u003C\u002Fcode> を使って、リポジトリの実体を指すリンクを張っている。\u003C\u002Fp>\n\u003Cfigure class=\"code\" data-lang=\"nix\">\u003Cdiv class=\"code-bar\">\u003Cspan class=\"lang\">NIX\u003C\u002Fspan>\u003Cspan>17 LINES\u003C\u002Fspan>\u003Cbutton class=\"copy\" type=\"button\" data-cursor=\"COPY\">COPY\u003C\u002Fbutton>\u003C\u002Fdiv>\u003Cpre class=\"shiki site-code\" style=\"background-color:var(--shiki-background);color:var(--shiki-foreground)\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan># home\u002Fdotfiles.nix\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>{ config, hostSpec, ... }:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>let\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  mkLink = path: config.lib.file.mkOutOfStoreSymlink \"${hostSpec.dotfilesDir}\u002F${path}\";\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>in\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>{\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  home.file = {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    \".zshenv\".source = mkLink \"shell\u002Fzshenv\";\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    \".zshrc\".source  = mkLink \"shell\u002Fzshrc\";\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  };\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  xdg.configFile = {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    \"nvim\".source     = mkLink \"config\u002Fnvim\";\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    \"git\".source      = mkLink \"config\u002Fgit\";\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    \"starship\".source = mkLink \"config\u002Fstarship\";\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  };\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Ffigure>\n\u003Cp>\u003Ccode>.zshrc\u003C\u002Fcode> は \u003Ccode>.zshrc\u003C\u002Fcode> のまま、Git 管理下の素のシェルスクリプトとして残る。リポジトリを編集した瞬間に反映されるので、1 文字直すたびに再ビルド、ということもない。\u003C\u002Fp>\n\u003Cp>Nix の導入は、これまでの dotfiles 運用を捨てて乗り換えるのではなく、その上に載せる形にできる。手書きスクリプトや stow で積んできた資産はそのまま生きる。\u003C\u002Fp>\n\u003Ch3 id=\"3-台を-1-つの設定で回す\" tabindex=\"-1\">3 台を 1 つの設定で回す\u003C\u002Fh3>\n\u003Cp>私の場合、最終的にこうなっている。\u003C\u002Fp>\n\u003Cdiv class=\"tbl\" tabindex=\"0\" role=\"region\" aria-label=\"Table\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>ホスト\u003C\u002Fth>\n\u003Cth>用途\u003C\u002Fth>\n\u003Cth>Nix の適用範囲\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>MacBook Pro\u003C\u002Ftd>\n\u003Ctd>メイン機\u003C\u002Ftd>\n\u003Ctd>CLI + dotfiles + GUI アプリ\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Mac mini\u003C\u002Ftd>\n\u003Ctd>ヘッドレスの常時稼働機\u003C\u002Ftd>\n\u003Ctd>ほぼ全体（常駐デーモンの定義まで）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>会社 Windows の WSL2\u003C\u002Ftd>\n\u003Ctd>予備\u003C\u002Ftd>\n\u003Ctd>WSL2 内部のみ（Windows 本体は対象外）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>Windows が混ざっているが、WSL2 の中身は Linux なので同じ仕組みが使える。構成の作り方は macOS 側と変わる（macOS は nix-darwin、WSL2 は home-manager 単体）。それでも共通のユーザー設定層は 3 台でそのまま共有できている。ここは一番不安だった部分だが、やってみたら一番あっさり通った。\u003C\u002Fp>\n\u003Ch3 id=\"正直に言うと、nix-で楽にならないこと\" tabindex=\"-1\">正直に言うと、Nix で楽にならないこと\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>新端末構築は「1 コマンド」にはならない。\u003C\u002Fstrong> 私の設計目標は「現状 8 手順・順序が暗黙 → 1 コマンド」だったが、実際にまっさらな Mac から復元する手順は今もこうなっている。\u003C\u002Fp>\n\u003Col>\n\u003Cli>Xcode Command Line Tools を入れる\u003C\u002Fli>\n\u003Cli>Homebrew 本体を入れる（Nix はこれを入れてくれない）\u003C\u002Fli>\n\u003Cli>GitHub 認証を設定する\u003C\u002Fli>\n\u003Cli>Nix 本体を入れる\u003C\u002Fli>\n\u003Cli>dotfiles を clone する\u003C\u002Fli>\n\u003Cli>反映コマンドを 1 回打つ\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>前提手順が 5 つ残っている。手順数でいえば 8 が 6 になった程度だ。\u003C\u002Fp>\n\u003Cp>短縮の実体は手順数ではない。変わったのは、この手順と順序がリポジトリに書いてあることだ。移行前は私の記憶の中にしか無かった。思い出しながらやる作業が、読みながらやる作業になった。実感としてはそれが一番大きい。\u003C\u002Fp>\n\u003Cp>他にも正直なところを書いておく。\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Nix 言語の学習コストはある。\u003C\u002Fstrong> 設定内容を書き直さない方針にしても、flake の外枠は書けるようにならないと始まらない\u003C\u002Fli>\n\u003Cli>\u003Cstrong>ロールバックは設定の中身を戻さない。\u003C\u002Fstrong> 世代を戻してもリンク先はリポジトリの現在の内容なので、設定を戻すのは \u003Ccode>git\u003C\u002Fcode> の仕事になる\u003C\u002Fli>\n\u003Cli>\u003Cstrong>ディスクを食う。\u003C\u002Fstrong> 世代を溜めると \u003Ccode>\u002Fnix\u002Fstore\u003C\u002Fcode> が膨らむので、定期的な GC の設定が要る\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Chr>\n\u003Ch2 id=\"始め方\" tabindex=\"-1\">\u003Cspan class=\"h-no\" aria-hidden=\"true\">07\u003C\u002Fspan>始め方\u003C\u002Fh2>\n\u003Ch3 id=\"今日（30-分）\" tabindex=\"-1\">今日（30 分）\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>1. リポジトリを作る。\u003C\u002Fstrong> \u003Ccode>dotfiles\u003C\u002Fcode> という名前で GitHub に作る。public でいい（秘密情報を入れなければ、の話だが）。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>2. 一番よく触るファイルを 1 つだけ入れる。\u003C\u002Fstrong> \u003Ccode>.zshrc\u003C\u002Fcode> でも \u003Ccode>.gitconfig\u003C\u002Fcode> でもいい。\u003C\u002Fp>\n\u003Cfigure class=\"code\" data-lang=\"sh\">\u003Cdiv class=\"code-bar\">\u003Cspan class=\"lang\">SH\u003C\u002Fspan>\u003Cspan>3 LINES\u003C\u002Fspan>\u003Cbutton class=\"copy\" type=\"button\" data-cursor=\"COPY\">COPY\u003C\u002Fbutton>\u003C\u002Fdiv>\u003Cpre class=\"shiki site-code\" style=\"background-color:var(--shiki-background);color:var(--shiki-foreground)\" tabindex=\"0\">\u003Ccode class=\"language-sh\">\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-token-function)\">mkdir\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> -p\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> ~\u002Fghq\u002Fgithub.com\u002F\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-keyword)\">&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\">use\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-foreground)\">r\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-keyword)\">>\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\">\u002Fdotfiles\u002Fshell\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-token-function)\">mv\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> ~\u002F.zshrc\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> ~\u002Fghq\u002Fgithub.com\u002F\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-keyword)\">&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\">use\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-foreground)\">r\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-keyword)\">>\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\">\u002Fdotfiles\u002Fshell\u002Fzshrc\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-token-function)\">ln\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> -sfn\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> ~\u002Fghq\u002Fgithub.com\u002F\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-keyword)\">&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\">use\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-foreground)\">r\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-keyword)\">>\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\">\u002Fdotfiles\u002Fshell\u002Fzshrc\u003C\u002Fspan>\u003Cspan style=\"color:var(--shiki-token-string)\"> ~\u002F.zshrc\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Ffigure>\n\u003Cp>新しいシェルを開いて、今までどおり動くことを確認する。動いたらコミットする。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>3. \u003Ccode>.gitignore\u003C\u002Fcode> を書く。\u003C\u002Fstrong> 秘密情報を入れないための最初の防御になる。\u003C\u002Fp>\n\u003Cfigure class=\"code\" data-lang=\"gitignore\">\u003Cdiv class=\"code-bar\">\u003Cspan class=\"lang\">GITIGNORE\u003C\u002Fspan>\u003Cspan>5 LINES\u003C\u002Fspan>\u003Cbutton class=\"copy\" type=\"button\" data-cursor=\"COPY\">COPY\u003C\u002Fbutton>\u003C\u002Fdiv>\u003Cpre class=\"shiki site-code\" style=\"background-color:var(--shiki-background);color:var(--shiki-foreground)\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>*.key\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>*.pem\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>.env\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>id_*\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>!id_*.pub\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Ffigure>\n\u003Ch3 id=\"今週\" tabindex=\"-1\">今週\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>残りの設定ファイルを順に移す。\u003Cstrong>一度に全部やらない。\u003C\u002Fstrong> 1 つ移してはシェルを開き直す、を繰り返す\u003C\u002Fli>\n\u003Cli>リンクを張るスクリプト（前述の方法 1）を書いて、\u003Ccode>scripts\u002Flink.sh\u003C\u002Fcode> に置く\u003C\u002Fli>\n\u003Cli>README に「新端末での復元手順」を書き始める。この時点では不完全でいい\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 id=\"今月\" tabindex=\"-1\">今月\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>2 台目の端末に clone して、実際に復元してみる。\u003Cstrong>ここで README の穴が全部見つかる\u003C\u002Fstrong>\u003C\u002Fli>\n\u003Cli>端末固有の設定が出てきたら、その分離方法を決める（\u003Ccode>zshrc.local\u003C\u002Fcode> を \u003Ccode>.gitignore\u003C\u002Fcode> する、など）\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 id=\"その先\" tabindex=\"-1\">その先\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>ツール本体の管理が面倒になってきたら、\u003Ccode>Brewfile\u003C\u002Fcode> か Nix を検討する\u003C\u002Fli>\n\u003Cli>端末が 3 台以上になった、あるいはシェルスクリプトの \u003Ccode>if\u003C\u002Fcode> が増えてきたら、Nix を検討するタイミングだと思う\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>端末が 1 台で \u003Ccode>if\u003C\u002Fcode> も生えていないなら、Nix はまだ要らない。\u003C\u002Fstrong> dotfiles 運用だけで得られるものが十分に大きいので、そこで止めていい。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"まとめ\" tabindex=\"-1\">\u003Cspan class=\"h-no\" aria-hidden=\"true\">08\u003C\u002Fspan>まとめ\u003C\u002Fh2>\n\u003Cp>設定ファイルを端末に置かず、Git リポジトリに移してリンクを張る。これだけで新端末での復元・変更履歴・端末間の統一が同時に手に入る。秘密情報を入れないことと、配線は手書きスクリプトから始めていいこと。最初に押さえるのはこのくらいでいい。\u003C\u002Fp>\n\u003Cp>それでも穴は残る。設定ファイルは揃ってもツール本体は揃わず、端末差分はシェルの \u003Ccode>if\u003C\u002Fcode> に溜まり、手順は暗黙のままになる。Nix はそこを埋める。既存の dotfiles を書き直さずに載せられるのも大きい。ただし「1 コマンドで新端末構築」には届かない。変わるのは手順の数ではなく、手順が記録されているかどうかだ。\u003C\u002Fp>\n\u003Cp>正直に言うと、Nix を入れて一番効いたのは技術的な部分ではなかった気がしている。「なぜこうしたか」を書き残す習慣がついたことの方が大きい。設定そのものより、判断の履歴の方が後で効いてくる。もっともこれが Nix のおかげなのか、単に 3 台目で必要に迫られただけなのかは、まだ私自身、切り分けられていない。\u003C\u002Fp>\n\u003Cp>私の構成は \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fyoshihiko555\u002Fdotfiles\" target=\"_blank\" rel=\"noopener noreferrer\">yoshihiko555\u002Fdotfiles\u003C\u002Fa> で公開している。Nix の部分は \u003Ccode>config\u002Fnix\u002F\u003C\u002Fcode> 以下にあり、判断の経緯は同ディレクトリの \u003Ccode>docs\u002F\u003C\u002Fcode> に ADR として残してある。\u003C\u002Fp>\n",[461,463,466,468,470,472,474,476,478,481,484,487,489,491,493,496,498,501,504,507,510,512,515,517,519,521],{"level":426,"id":462,"text":462},"結論から",{"level":426,"id":464,"text":465},"dotfiles-とは何か","dotfiles とは何か",{"level":419,"id":467,"text":467},"端末に置いたままだと何が起きるか",{"level":426,"id":469,"text":469},"何を入れて、何を入れないか",{"level":419,"id":471,"text":471},"入れるもの",{"level":419,"id":473,"text":473},"絶対に入れてはいけないもの",{"level":419,"id":475,"text":475},"迷うもの",{"level":426,"id":477,"text":477},"端末へどう配線するか",{"level":419,"id":479,"text":480},"方法-1:-手書きのシェルスクリプト","方法 1: 手書きのシェルスクリプト",{"level":419,"id":482,"text":483},"方法-2:-gnu-stow","方法 2: GNU stow",{"level":419,"id":485,"text":486},"方法-3:-nix（home-manager）","方法 3: Nix（home-manager）",{"level":419,"id":488,"text":488},"どれを選ぶか",{"level":426,"id":490,"text":490},"運用してみると見えてくる穴",{"level":419,"id":492,"text":492},"設定ファイルは揃うが、ツール本体は揃わない",{"level":419,"id":494,"text":495},"端末差分がシェルスクリプトの-if-に溜まる","端末差分がシェルスクリプトの if に溜まる",{"level":419,"id":497,"text":497},"手順の順序が、結局まだ暗黙のまま",{"level":426,"id":499,"text":500},"プラスアルファ:-ここで-nix-を足す","プラスアルファ: ここで Nix を足す",{"level":419,"id":502,"text":503},"nix-が-dotfiles-に足すもの","Nix が dotfiles に足すもの",{"level":419,"id":505,"text":506},"3-台を-1-つの設定で回す","3 台を 1 つの設定で回す",{"level":419,"id":508,"text":509},"正直に言うと、nix-で楽にならないこと","正直に言うと、Nix で楽にならないこと",{"level":426,"id":511,"text":511},"始め方",{"level":419,"id":513,"text":514},"今日（30-分）","今日（30 分）",{"level":419,"id":516,"text":516},"今週",{"level":419,"id":518,"text":518},"今月",{"level":419,"id":520,"text":520},"その先",{"level":426,"id":522,"text":522},"まとめ",14,{"slug":88,"title":89,"description":90,"publishedAt":91,"updatedAt":91,"category":525,"tags":526,"thumbnail":530},{"name":11,"slug":12},[527,528,529],{"name":95,"slug":96},{"name":98,"slug":99},{"name":101,"slug":101},{"url":103,"description":104},{"slug":61,"title":62,"description":63,"publishedAt":64,"updatedAt":64,"category":532,"tags":533,"thumbnail":16},{"name":49,"slug":50},[534,535,536,537],{"name":58,"slug":59},{"name":69,"slug":69},{"name":71,"slug":71},{"name":73,"slug":73},[539,545,552],{"slug":44,"title":45,"description":46,"publishedAt":47,"updatedAt":47,"category":540,"tags":541,"thumbnail":16},{"name":49,"slug":50},[542,543,544],{"name":53,"slug":53},{"name":55,"slug":56},{"name":58,"slug":59},{"slug":61,"title":62,"description":63,"publishedAt":64,"updatedAt":64,"category":546,"tags":547,"thumbnail":16},{"name":49,"slug":50},[548,549,550,551],{"name":58,"slug":59},{"name":69,"slug":69},{"name":71,"slug":71},{"name":73,"slug":73},{"slug":168,"title":169,"description":170,"publishedAt":171,"updatedAt":172,"category":553,"tags":554,"thumbnail":557},{"name":49,"slug":50},[555,556],{"name":25,"slug":26},{"name":58,"slug":59},{"url":178,"description":104},1793725976670]