<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Workflow on Tarragon</title><link>https://tarrragon.github.io/blog/tags/workflow/</link><description>Recent content in Workflow on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Tue, 30 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/workflow/index.xml" rel="self" type="application/rss+xml"/><item><title>浮動式 vs 平鋪式視窗管理</title><link>https://tarrragon.github.io/blog/linux/dotfile/04-window-management/floating-vs-tiling/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/04-window-management/floating-vs-tiling/</guid><description>&lt;p>桌面視窗管理分成兩種基本模式。&lt;/p>
&lt;p>&lt;strong>浮動式（floating）&lt;/strong> 是多數人熟悉的模式。視窗可以重疊、可以任意拖拉調整大小、可以最小化藏起來。macOS、Windows、GNOME、KDE 預設都是浮動式。操作直覺是「每個視窗是一張紙，自己決定放哪裡」。&lt;/p>
&lt;p>&lt;strong>平鋪式（tiling）&lt;/strong> 的規則不同：視窗自動排列填滿螢幕、不重疊，由 WM 的規則決定版面怎麼切割。開一個新視窗時，WM 自動把現有空間分一半給它；關掉一個視窗時，相鄰視窗自動擴展填補。操作直覺是「螢幕是一塊蛋糕，WM 負責切」。&lt;/p>
&lt;p>多數平鋪式 WM 支援&lt;strong>混合模式&lt;/strong>：特定視窗可以設為浮動，脫離平鋪規則。設定面板、密碼輸入框、小工具這類不適合塞進格子的視窗，通常會設成浮動例外。平鋪是預設，浮動是按需啟用的例外。&lt;/p>
&lt;h2 id="手動貼齊-vs-自動平鋪">手動貼齊 vs 自動平鋪&lt;/h2>
&lt;p>在進入平鋪式 WM 之前，macOS 和 Windows 都提供了「手動貼齊」功能——用快捷鍵或拖拉把視窗貼到螢幕的半邊、角落、三分之一。macOS 原生的 window snapping、Windows 的 Snap Layout、以及 Rectangle 和 Magnet 這類第三方工具都屬於這個範疇。&lt;/p>
&lt;p>手動貼齊跟自動平鋪的差距，在視窗數量少的時候幾乎感覺不到。開兩個視窗、左右各半，手動按一下快捷鍵就到位，完全夠用。&lt;/p>
&lt;p>差距在視窗數量多的時候才出現。每開一個新視窗都要決定它放哪、按對應的快捷鍵；關掉一個視窗就留下空洞，要手動拖拉其他視窗去填——當這種版面管理的決策負擔開始分散你對工作本身的注意力時，就是自動平鋪開始有價值的時機。這個斷點因螢幕大小和工作類型而異：23 吋單螢幕上同時開終端機、編輯器、瀏覽器、文件，版面很快就不夠分；雙螢幕寬螢幕上同樣的視窗數量可能還很從容。&lt;/p>
&lt;p>自動平鋪在這個情境下的優勢有三層。&lt;/p>
&lt;p>第一層是&lt;strong>自動回填&lt;/strong>。開視窗、關視窗，WM 自動重新分配空間，版面永遠是滿的、整齊的。你不用做任何版面決策。&lt;/p>
&lt;p>第二層是&lt;strong>操作對象的轉換&lt;/strong>。手動貼齊的操作對象是「某個視窗」——把 A 視窗貼到左邊、把 B 視窗貼到右上。平鋪式 WM 的操作對象是「版面結構」——把焦點往右移、把當前視窗跟隔壁交換、把這一格再水平切一半。你操作的是位置關係，不是絕對座標。&lt;/p>
&lt;p>第三層是&lt;strong>工作區整合&lt;/strong>。平鋪式工作流通常搭配多個工作區（workspace），每個工作區是一套獨立的平鋪佈局。「編輯器和終端機在工作區 1、瀏覽器在工作區 2、通訊軟體在工作區 4」——用快捷鍵瞬間切換整套上下文，而不是在一堆重疊視窗裡找。手動貼齊工具通常不帶工作區管理。&lt;/p>
&lt;h2 id="適用判讀">適用判讀&lt;/h2>
&lt;p>平鋪式視窗管理的投資報酬率取決於你的工作型態。&lt;/p>
&lt;p>&lt;strong>高回報情境&lt;/strong>：經常同時操作多個視窗且版面管理開始分散注意力、多數是「方方正正、可平鋪」的 app（終端機、編輯器、瀏覽器、文件閱讀器）、鍵盤操作為主、多螢幕、工作需要頻繁切換上下文（多個專案、不同任務區）。&lt;/p>
&lt;p>&lt;strong>低回報情境&lt;/strong>：大量使用需要特定比例或自由拖拉的 app（設計工具、影片剪輯、簡報製作）、很少同時開多個視窗、已經習慣且滿意目前的工作流、不想花時間學新鍵位。&lt;/p>
&lt;p>&lt;strong>折衷方案&lt;/strong>：所有平鋪式工具都支援 per-app 的浮動例外。不適合平鋪的 app（設定面板、計算機、某些對話框）設成浮動，其餘維持平鋪。這不是全有全無的選擇。&lt;/p>
&lt;p>一個常見的踩坑模式是：看到 Hyprland 的截圖很漂亮，衝動裝了，發現日常有一半 app 不適合平鋪、鍵位記不住、每次更新都要修配置，兩週後放棄。務實的進入路徑是先在目前的系統上試手動貼齊工具（macOS 的 Rectangle 或 AeroSpace），確認自己真的享受鍵盤操作視窗的節奏，再往 Linux tiling WM 推進。或者用 VM 跑 Hyprland 體驗看看——體驗打折（VM 沒有 GPU 加速，動畫會卡），但能確認自己是否喜歡這種操作邏輯，再決定要不要花時間在實體機上搭建。&lt;/p></description><content:encoded><![CDATA[<p>桌面視窗管理分成兩種基本模式。</p>
<p><strong>浮動式（floating）</strong> 是多數人熟悉的模式。視窗可以重疊、可以任意拖拉調整大小、可以最小化藏起來。macOS、Windows、GNOME、KDE 預設都是浮動式。操作直覺是「每個視窗是一張紙，自己決定放哪裡」。</p>
<p><strong>平鋪式（tiling）</strong> 的規則不同：視窗自動排列填滿螢幕、不重疊，由 WM 的規則決定版面怎麼切割。開一個新視窗時，WM 自動把現有空間分一半給它；關掉一個視窗時，相鄰視窗自動擴展填補。操作直覺是「螢幕是一塊蛋糕，WM 負責切」。</p>
<p>多數平鋪式 WM 支援<strong>混合模式</strong>：特定視窗可以設為浮動，脫離平鋪規則。設定面板、密碼輸入框、小工具這類不適合塞進格子的視窗，通常會設成浮動例外。平鋪是預設，浮動是按需啟用的例外。</p>
<h2 id="手動貼齊-vs-自動平鋪">手動貼齊 vs 自動平鋪</h2>
<p>在進入平鋪式 WM 之前，macOS 和 Windows 都提供了「手動貼齊」功能——用快捷鍵或拖拉把視窗貼到螢幕的半邊、角落、三分之一。macOS 原生的 window snapping、Windows 的 Snap Layout、以及 Rectangle 和 Magnet 這類第三方工具都屬於這個範疇。</p>
<p>手動貼齊跟自動平鋪的差距，在視窗數量少的時候幾乎感覺不到。開兩個視窗、左右各半，手動按一下快捷鍵就到位，完全夠用。</p>
<p>差距在視窗數量多的時候才出現。每開一個新視窗都要決定它放哪、按對應的快捷鍵；關掉一個視窗就留下空洞，要手動拖拉其他視窗去填——當這種版面管理的決策負擔開始分散你對工作本身的注意力時，就是自動平鋪開始有價值的時機。這個斷點因螢幕大小和工作類型而異：23 吋單螢幕上同時開終端機、編輯器、瀏覽器、文件，版面很快就不夠分；雙螢幕寬螢幕上同樣的視窗數量可能還很從容。</p>
<p>自動平鋪在這個情境下的優勢有三層。</p>
<p>第一層是<strong>自動回填</strong>。開視窗、關視窗，WM 自動重新分配空間，版面永遠是滿的、整齊的。你不用做任何版面決策。</p>
<p>第二層是<strong>操作對象的轉換</strong>。手動貼齊的操作對象是「某個視窗」——把 A 視窗貼到左邊、把 B 視窗貼到右上。平鋪式 WM 的操作對象是「版面結構」——把焦點往右移、把當前視窗跟隔壁交換、把這一格再水平切一半。你操作的是位置關係，不是絕對座標。</p>
<p>第三層是<strong>工作區整合</strong>。平鋪式工作流通常搭配多個工作區（workspace），每個工作區是一套獨立的平鋪佈局。「編輯器和終端機在工作區 1、瀏覽器在工作區 2、通訊軟體在工作區 4」——用快捷鍵瞬間切換整套上下文，而不是在一堆重疊視窗裡找。手動貼齊工具通常不帶工作區管理。</p>
<h2 id="適用判讀">適用判讀</h2>
<p>平鋪式視窗管理的投資報酬率取決於你的工作型態。</p>
<p><strong>高回報情境</strong>：經常同時操作多個視窗且版面管理開始分散注意力、多數是「方方正正、可平鋪」的 app（終端機、編輯器、瀏覽器、文件閱讀器）、鍵盤操作為主、多螢幕、工作需要頻繁切換上下文（多個專案、不同任務區）。</p>
<p><strong>低回報情境</strong>：大量使用需要特定比例或自由拖拉的 app（設計工具、影片剪輯、簡報製作）、很少同時開多個視窗、已經習慣且滿意目前的工作流、不想花時間學新鍵位。</p>
<p><strong>折衷方案</strong>：所有平鋪式工具都支援 per-app 的浮動例外。不適合平鋪的 app（設定面板、計算機、某些對話框）設成浮動，其餘維持平鋪。這不是全有全無的選擇。</p>
<p>一個常見的踩坑模式是：看到 Hyprland 的截圖很漂亮，衝動裝了，發現日常有一半 app 不適合平鋪、鍵位記不住、每次更新都要修配置，兩週後放棄。務實的進入路徑是先在目前的系統上試手動貼齊工具（macOS 的 Rectangle 或 AeroSpace），確認自己真的享受鍵盤操作視窗的節奏，再往 Linux tiling WM 推進。或者用 VM 跑 Hyprland 體驗看看——體驗打折（VM 沒有 GPU 加速，動畫會卡），但能確認自己是否喜歡這種操作邏輯，再決定要不要花時間在實體機上搭建。</p>
]]></content:encoded></item><item><title>環境可重現性與配置分類</title><link>https://tarrragon.github.io/blog/linux/dotfile/00-dotfile-mindset/environment-reproducibility/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/00-dotfile-mindset/environment-reproducibility/</guid><description>&lt;p>Dotfile 管理的核心能力是&lt;strong>環境可重現性&lt;/strong>：把個人開發環境的配置狀態變成版控下的代碼，讓任何一台空白機器都能用一份 Git repo 還原成你熟悉的工作桌面。&lt;/p>
&lt;h2 id="什麼是-dotfile">什麼是 Dotfile&lt;/h2>
&lt;p>Unix 系統用檔名開頭的 &lt;code>.&lt;/code> 標記隱藏檔。shell 配置（&lt;code>.bashrc&lt;/code>、&lt;code>.zshrc&lt;/code>）、Git 設定（&lt;code>.gitconfig&lt;/code>）、SSH 設定（&lt;code>.ssh/config&lt;/code>）、以及 &lt;code>~/.config/&lt;/code> 底下各種工具的配置目錄，都屬於這個範疇。這些檔案決定了你的工作環境怎麼運作：shell 的 prompt 長什麼樣、alias 有哪些、editor 用什麼 keymap、terminal 的配色方案、視窗管理器怎麼排列畫面。&lt;/p>
&lt;p>「dotfile 管理」指的是把這些散落在家目錄各處的配置檔&lt;strong>集中到一個 Git repo&lt;/strong>，建立版本歷史、可以跨機器同步、可以在新環境一鍵部署。跟手動備份的差異在於：備份是搬檔案，dotfile 管理是建立一套可重複執行的環境建構流程。&lt;/p>
&lt;h2 id="為什麼要管理-dotfile">為什麼要管理 Dotfile&lt;/h2>
&lt;p>開發環境是累積出來的。今天加一個 alias、明天改一個 Git 設定、下週裝了一個 terminal 外掛調了字型。這些微調加起來就是「你順手的工作環境」，但因為都是零碎的小改動，很少有人會主動記錄。&lt;/p>
&lt;p>問題在累積到一定程度後、環境需要重建的那一刻才會浮出來：&lt;/p>
&lt;p>&lt;strong>換機器&lt;/strong>。拿到新筆電，開始從零設定。裝完 shell、editor、terminal，發現少了一堆 alias 和 function，但想不起來之前到底加了哪些。花兩天勉強恢復到七八成，剩下的在未來幾週慢慢「撞到才發現少了」。&lt;/p>
&lt;p>&lt;strong>設備故障或遺失&lt;/strong>。公司配的筆電硬碟壞了。如果配置沒有外部副本，那台機器上所有的自訂設定（有些可能花了半年慢慢調出來的）全部歸零。復原速度直接取決於「你有沒有把配置存在機器以外的地方」。&lt;/p>
&lt;p>&lt;strong>在 VM 或容器裡重現環境&lt;/strong>。想在虛擬機裡測試一套 Linux 桌面（例如 Hyprland），或在 Docker 容器裡重現自己的 shell 環境做 CI 除錯。沒有版控的配置，就得手動複製貼上，還要記住哪些檔案在哪個路徑。&lt;/p>
&lt;p>&lt;strong>跨機器一致性&lt;/strong>。同時用筆電、桌機、遠端伺服器，希望每台機器的 shell 行為、Git 設定、editor 快捷鍵都一致。手動同步的成本隨機器數量線性增長，而且很容易漏改某一台，導致操作習慣在不同機器間不一致。&lt;/p>
&lt;p>這些場景的共通點是：&lt;strong>配置的價值在累積，但累積的前提是有記錄&lt;/strong>。沒有記錄的累積，只是暫存在某一台機器上的隱性知識，機器一換就歸零。&lt;/p>
&lt;h2 id="哪些東西應該建立-dotfile">哪些東西應該建立 Dotfile&lt;/h2>
&lt;p>依配置的普遍性和適用場景，分成三層來判斷：&lt;/p>
&lt;h3 id="核心層幾乎所有開發者都該管的">核心層：幾乎所有開發者都該管的&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Shell 配置&lt;/strong>（&lt;code>.zshrc&lt;/code> / &lt;code>.bashrc&lt;/code>）：alias、function、PATH、prompt、completion 設定。這是最高頻修改、也最容易累積隱性知識的地方。&lt;/li>
&lt;li>&lt;strong>Git 配置&lt;/strong>（&lt;code>.gitconfig&lt;/code>）：使用者名稱、email、預設 editor、alias（&lt;code>git lg&lt;/code> = &lt;code>git log --oneline --graph&lt;/code>）、diff/merge tool。&lt;/li>
&lt;li>&lt;strong>SSH 配置&lt;/strong>（&lt;code>.ssh/config&lt;/code>）：Host 別名、ProxyJump 跳板設定、每台主機的 IdentityFile 指定。注意：&lt;strong>config 檔進 repo，私鑰不進 repo&lt;/strong>。&lt;/li>
&lt;li>&lt;strong>Editor 配置&lt;/strong>：&lt;code>.vimrc&lt;/code>（Vim）、&lt;code>~/.config/nvim/&lt;/code>（Neovim）、VS Code 的 &lt;code>settings.json&lt;/code> / &lt;code>keybindings.json&lt;/code>。&lt;/li>
&lt;/ul>
&lt;h3 id="工具層依個人工具鏈而定">工具層：依個人工具鏈而定&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Terminal multiplexer&lt;/strong>：tmux（&lt;code>.tmux.conf&lt;/code>）、zellij（&lt;code>~/.config/zellij/&lt;/code>）的面板配置、快捷鍵、狀態列。&lt;/li>
&lt;li>&lt;strong>Terminal emulator&lt;/strong>：Alacritty、WezTerm、Kitty 的字型、配色、快捷鍵設定。&lt;/li>
&lt;li>&lt;strong>套件清單&lt;/strong>：macOS 的 &lt;code>Brewfile&lt;/code>（&lt;code>brew bundle dump&lt;/code>）、Arch 的 &lt;code>pacman -Qqe &amp;gt; pkglist.txt&lt;/code>。這份清單讓新機器知道該裝哪些軟體。&lt;/li>
&lt;li>&lt;strong>開發工具的全域配置&lt;/strong>：&lt;code>.npmrc&lt;/code>、&lt;code>.cargo/config.toml&lt;/code>、&lt;code>.pypirc&lt;/code> 等。但要注意區分：&lt;code>.eslintrc&lt;/code> / &lt;code>.prettierrc&lt;/code> 這類通常跟著&lt;strong>專案&lt;/strong> repo 走（每個專案可能規則不同），不跟著人走。&lt;/li>
&lt;/ul>
&lt;h3 id="桌面層linux-桌面環境才需要">桌面層：Linux 桌面環境才需要&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Window manager&lt;/strong>：Hyprland（&lt;code>~/.config/hypr/&lt;/code>）、i3（&lt;code>~/.config/i3/&lt;/code>）、sway 的配置。&lt;/li>
&lt;li>&lt;strong>桌面元件&lt;/strong>：狀態列（waybar）、應用程式啟動器（rofi / wofi）、通知服務（mako / dunst）、鎖屏（swaylock / hyprlock）。&lt;/li>
&lt;li>&lt;strong>主題與配色&lt;/strong>：GTK / Qt 主題設定、游標主題、字型配置。&lt;/li>
&lt;/ul>
&lt;h3 id="判讀準則">判讀準則&lt;/h3>
&lt;p>區分一個配置該不該進 dotfile repo，核心問題是：&lt;strong>這個設定是跟著人走，還是跟著專案走？&lt;/strong>&lt;/p>
&lt;p>跟著人走的（不管開哪個專案都要用的）→ 進 dotfile repo。跟著專案走的（專案 A 用 ESLint、專案 B 用 Biome）→ 留在專案 repo。&lt;/p>
&lt;h3 id="不該進-dotfile-repo-的">不該進 Dotfile Repo 的&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>私鑰、API key、token、密碼&lt;/strong>。用 &lt;code>.gitignore&lt;/code> 排除，敏感資訊放 secret manager 或加密管理（具體做法見&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/08-sync-bootstrap/sync-strategy-secret/" data-link-title="跨機器同步、Secret 管理與環境重建流程" data-link-desc="多台機器的 dotfile 怎麼同步、哪些東西不該進 repo 時回來讀">同步與 Secret 管理&lt;/a>）。&lt;/li>
&lt;li>&lt;strong>暫存檔、cache、log&lt;/strong>。&lt;code>.zsh_history&lt;/code> 很大且含敏感指令；各種工具的 cache 目錄是 generated 檔案，重建時自動產生。&lt;/li>
&lt;li>&lt;strong>OS 層級的二進位設定&lt;/strong>。macOS 的 plist 可以選擇性管理（&lt;code>defaults write&lt;/code> 指令可以版控），但整個 &lt;code>~/Library/Preferences/&lt;/code> 不適合直接丟進 Git——檔案格式不穩定、diff 不可讀、很多是應用程式自動產生的。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Dotfile 管理的核心能力是<strong>環境可重現性</strong>：把個人開發環境的配置狀態變成版控下的代碼，讓任何一台空白機器都能用一份 Git repo 還原成你熟悉的工作桌面。</p>
<h2 id="什麼是-dotfile">什麼是 Dotfile</h2>
<p>Unix 系統用檔名開頭的 <code>.</code> 標記隱藏檔。shell 配置（<code>.bashrc</code>、<code>.zshrc</code>）、Git 設定（<code>.gitconfig</code>）、SSH 設定（<code>.ssh/config</code>）、以及 <code>~/.config/</code> 底下各種工具的配置目錄，都屬於這個範疇。這些檔案決定了你的工作環境怎麼運作：shell 的 prompt 長什麼樣、alias 有哪些、editor 用什麼 keymap、terminal 的配色方案、視窗管理器怎麼排列畫面。</p>
<p>「dotfile 管理」指的是把這些散落在家目錄各處的配置檔<strong>集中到一個 Git repo</strong>，建立版本歷史、可以跨機器同步、可以在新環境一鍵部署。跟手動備份的差異在於：備份是搬檔案，dotfile 管理是建立一套可重複執行的環境建構流程。</p>
<h2 id="為什麼要管理-dotfile">為什麼要管理 Dotfile</h2>
<p>開發環境是累積出來的。今天加一個 alias、明天改一個 Git 設定、下週裝了一個 terminal 外掛調了字型。這些微調加起來就是「你順手的工作環境」，但因為都是零碎的小改動，很少有人會主動記錄。</p>
<p>問題在累積到一定程度後、環境需要重建的那一刻才會浮出來：</p>
<p><strong>換機器</strong>。拿到新筆電，開始從零設定。裝完 shell、editor、terminal，發現少了一堆 alias 和 function，但想不起來之前到底加了哪些。花兩天勉強恢復到七八成，剩下的在未來幾週慢慢「撞到才發現少了」。</p>
<p><strong>設備故障或遺失</strong>。公司配的筆電硬碟壞了。如果配置沒有外部副本，那台機器上所有的自訂設定（有些可能花了半年慢慢調出來的）全部歸零。復原速度直接取決於「你有沒有把配置存在機器以外的地方」。</p>
<p><strong>在 VM 或容器裡重現環境</strong>。想在虛擬機裡測試一套 Linux 桌面（例如 Hyprland），或在 Docker 容器裡重現自己的 shell 環境做 CI 除錯。沒有版控的配置，就得手動複製貼上，還要記住哪些檔案在哪個路徑。</p>
<p><strong>跨機器一致性</strong>。同時用筆電、桌機、遠端伺服器，希望每台機器的 shell 行為、Git 設定、editor 快捷鍵都一致。手動同步的成本隨機器數量線性增長，而且很容易漏改某一台，導致操作習慣在不同機器間不一致。</p>
<p>這些場景的共通點是：<strong>配置的價值在累積，但累積的前提是有記錄</strong>。沒有記錄的累積，只是暫存在某一台機器上的隱性知識，機器一換就歸零。</p>
<h2 id="哪些東西應該建立-dotfile">哪些東西應該建立 Dotfile</h2>
<p>依配置的普遍性和適用場景，分成三層來判斷：</p>
<h3 id="核心層幾乎所有開發者都該管的">核心層：幾乎所有開發者都該管的</h3>
<ul>
<li><strong>Shell 配置</strong>（<code>.zshrc</code> / <code>.bashrc</code>）：alias、function、PATH、prompt、completion 設定。這是最高頻修改、也最容易累積隱性知識的地方。</li>
<li><strong>Git 配置</strong>（<code>.gitconfig</code>）：使用者名稱、email、預設 editor、alias（<code>git lg</code> = <code>git log --oneline --graph</code>）、diff/merge tool。</li>
<li><strong>SSH 配置</strong>（<code>.ssh/config</code>）：Host 別名、ProxyJump 跳板設定、每台主機的 IdentityFile 指定。注意：<strong>config 檔進 repo，私鑰不進 repo</strong>。</li>
<li><strong>Editor 配置</strong>：<code>.vimrc</code>（Vim）、<code>~/.config/nvim/</code>（Neovim）、VS Code 的 <code>settings.json</code> / <code>keybindings.json</code>。</li>
</ul>
<h3 id="工具層依個人工具鏈而定">工具層：依個人工具鏈而定</h3>
<ul>
<li><strong>Terminal multiplexer</strong>：tmux（<code>.tmux.conf</code>）、zellij（<code>~/.config/zellij/</code>）的面板配置、快捷鍵、狀態列。</li>
<li><strong>Terminal emulator</strong>：Alacritty、WezTerm、Kitty 的字型、配色、快捷鍵設定。</li>
<li><strong>套件清單</strong>：macOS 的 <code>Brewfile</code>（<code>brew bundle dump</code>）、Arch 的 <code>pacman -Qqe &gt; pkglist.txt</code>。這份清單讓新機器知道該裝哪些軟體。</li>
<li><strong>開發工具的全域配置</strong>：<code>.npmrc</code>、<code>.cargo/config.toml</code>、<code>.pypirc</code> 等。但要注意區分：<code>.eslintrc</code> / <code>.prettierrc</code> 這類通常跟著<strong>專案</strong> repo 走（每個專案可能規則不同），不跟著人走。</li>
</ul>
<h3 id="桌面層linux-桌面環境才需要">桌面層：Linux 桌面環境才需要</h3>
<ul>
<li><strong>Window manager</strong>：Hyprland（<code>~/.config/hypr/</code>）、i3（<code>~/.config/i3/</code>）、sway 的配置。</li>
<li><strong>桌面元件</strong>：狀態列（waybar）、應用程式啟動器（rofi / wofi）、通知服務（mako / dunst）、鎖屏（swaylock / hyprlock）。</li>
<li><strong>主題與配色</strong>：GTK / Qt 主題設定、游標主題、字型配置。</li>
</ul>
<h3 id="判讀準則">判讀準則</h3>
<p>區分一個配置該不該進 dotfile repo，核心問題是：<strong>這個設定是跟著人走，還是跟著專案走？</strong></p>
<p>跟著人走的（不管開哪個專案都要用的）→ 進 dotfile repo。跟著專案走的（專案 A 用 ESLint、專案 B 用 Biome）→ 留在專案 repo。</p>
<h3 id="不該進-dotfile-repo-的">不該進 Dotfile Repo 的</h3>
<ul>
<li><strong>私鑰、API key、token、密碼</strong>。用 <code>.gitignore</code> 排除，敏感資訊放 secret manager 或加密管理（具體做法見<a href="/blog/linux/dotfile/08-sync-bootstrap/sync-strategy-secret/" data-link-title="跨機器同步、Secret 管理與環境重建流程" data-link-desc="多台機器的 dotfile 怎麼同步、哪些東西不該進 repo 時回來讀">同步與 Secret 管理</a>）。</li>
<li><strong>暫存檔、cache、log</strong>。<code>.zsh_history</code> 很大且含敏感指令；各種工具的 cache 目錄是 generated 檔案，重建時自動產生。</li>
<li><strong>OS 層級的二進位設定</strong>。macOS 的 plist 可以選擇性管理（<code>defaults write</code> 指令可以版控），但整個 <code>~/Library/Preferences/</code> 不適合直接丟進 Git——檔案格式不穩定、diff 不可讀、很多是應用程式自動產生的。</li>
</ul>
]]></content:encoded></item><item><title>Reflection / Self-critique</title><link>https://tarrragon.github.io/blog/llm/knowledge-cards/reflection/</link><pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/llm/knowledge-cards/reflection/</guid><description>&lt;p>Reflection（self-critique）的核心概念是「&lt;strong>模型先生成一個草版、再對自己的草版 critique、再修改&lt;/strong>」。屬於推理引導類的 prompting 技術、也是 &lt;a href="https://tarrragon.github.io/blog/llm/04-applications/workflow-patterns/" data-link-title="4.7 Workflow 編排模式" data-link-desc="Pipeline / router / parallel / reflection：多 LLM call 組合的四種基本模式與退化條件">workflow pattern&lt;/a> 的基本模式之一。跟 &lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/chain-of-thought/" data-link-title="Chain-of-Thought（CoT）" data-link-desc="讓 LLM 先輸出推理步驟再給最終答案的 prompting / 訓練方式、reasoning model 的基礎機制">chain-of-thought&lt;/a> 不同：CoT 是「過程要 explicit」、reflection 是「先寫一版再批評再改」、有明確的兩階段。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Reflection 三步：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">[Generate] 模型生成 v1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">[Critique] 模型（或 critic LLM）對 v1 給回饋
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">[Refine] 模型按回饋生成 v2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">(可選 loop)&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>跟其他模式對照：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>模式&lt;/th>
 &lt;th>結構&lt;/th>
 &lt;th>主要解的問題&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>CoT&lt;/td>
 &lt;td>Think step by step、單次生成&lt;/td>
 &lt;td>隱式推理變 explicit&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Reflection&lt;/td>
 &lt;td>Generate → critique → refine&lt;/td>
 &lt;td>一次生成不夠好、需要二次審視&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Multi-step&lt;/td>
 &lt;td>Retrieve / decide / retrieve again&lt;/td>
 &lt;td>資訊不足、要動態補資料&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>讀 prompt engineering / agent paper 看到「reflection」「self-critique」「self-refine」「critic」就是這個機制。實作判讀：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>適用模型有能力辨識「自己寫的不夠好」&lt;/strong>、critique 跟 generator 不會共用同樣 blind spot。&lt;/li>
&lt;li>&lt;strong>失敗在 systematic error&lt;/strong>：critique 跟 generator 是同個模型、訓練分佈中的盲點不會因為「再想一次」消失。判讀訊號：critique 每次給很像的建議、或修完還是同一類錯——換 critic 用不同 base model、或加外部驗證（test、lint、schema）取代 LLM critique。&lt;/li>
&lt;li>&lt;strong>失敗在低能力模型&lt;/strong>：critic 能力不足、產不出有用建議、徒增 cost / latency。&lt;/li>
&lt;li>&lt;strong>失敗在無限循環&lt;/strong>：沒有客觀停止訊號、reflection 一直跑、cost 爆掉。緩解：step cap + 外部 metric（test pass、schema valid）。&lt;/li>
&lt;li>&lt;strong>失敗在過度修正&lt;/strong>：每次 reflection 都改一點、累積結果變糟（過度 fitting critic 意見）。緩解：保留 baseline、reflection 結果要跟 baseline 比、不一定採用。&lt;/li>
&lt;/ol>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/agent-loop/" data-link-title="Agent Loop" data-link-desc="LLM agent 自我循環的工作流：LLM 規劃下一步、執行 tool、看結果、再規劃下一步、直到任務完成或停止條件觸發">Agent loop&lt;/a> 是 reflection 的延伸特例、進階失敗模式見 &lt;a href="https://tarrragon.github.io/blog/llm/04-applications/agent-architecture/" data-link-title="4.4 Agent 架構原理" data-link-desc="Agent loop 結構、失敗模式、什麼任務適合 vs 不適合、跟人類審查的協作模型">4.4 Agent 架構&lt;/a>。完整 workflow pattern 比較見 &lt;a href="https://tarrragon.github.io/blog/llm/04-applications/workflow-patterns/" data-link-title="4.7 Workflow 編排模式" data-link-desc="Pipeline / router / parallel / reflection：多 LLM call 組合的四種基本模式與退化條件">4.7 Workflow patterns&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>Reflection（self-critique）的核心概念是「<strong>模型先生成一個草版、再對自己的草版 critique、再修改</strong>」。屬於推理引導類的 prompting 技術、也是 <a href="/blog/llm/04-applications/workflow-patterns/" data-link-title="4.7 Workflow 編排模式" data-link-desc="Pipeline / router / parallel / reflection：多 LLM call 組合的四種基本模式與退化條件">workflow pattern</a> 的基本模式之一。跟 <a href="/blog/llm/knowledge-cards/chain-of-thought/" data-link-title="Chain-of-Thought（CoT）" data-link-desc="讓 LLM 先輸出推理步驟再給最終答案的 prompting / 訓練方式、reasoning model 的基礎機制">chain-of-thought</a> 不同：CoT 是「過程要 explicit」、reflection 是「先寫一版再批評再改」、有明確的兩階段。</p>
<h2 id="概念位置">概念位置</h2>
<p>Reflection 三步：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">[Generate]    模型生成 v1
</span></span><span class="line"><span class="ln">2</span><span class="cl">   ↓
</span></span><span class="line"><span class="ln">3</span><span class="cl">[Critique]    模型（或 critic LLM）對 v1 給回饋
</span></span><span class="line"><span class="ln">4</span><span class="cl">   ↓
</span></span><span class="line"><span class="ln">5</span><span class="cl">[Refine]      模型按回饋生成 v2
</span></span><span class="line"><span class="ln">6</span><span class="cl">   ↓
</span></span><span class="line"><span class="ln">7</span><span class="cl">(可選 loop)</span></span></code></pre></div><p>跟其他模式對照：</p>
<table>
  <thead>
      <tr>
          <th>模式</th>
          <th>結構</th>
          <th>主要解的問題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>CoT</td>
          <td>Think step by step、單次生成</td>
          <td>隱式推理變 explicit</td>
      </tr>
      <tr>
          <td>Reflection</td>
          <td>Generate → critique → refine</td>
          <td>一次生成不夠好、需要二次審視</td>
      </tr>
      <tr>
          <td>Multi-step</td>
          <td>Retrieve / decide / retrieve again</td>
          <td>資訊不足、要動態補資料</td>
      </tr>
  </tbody>
</table>
<h2 id="設計責任">設計責任</h2>
<p>讀 prompt engineering / agent paper 看到「reflection」「self-critique」「self-refine」「critic」就是這個機制。實作判讀：</p>
<ol>
<li><strong>適用模型有能力辨識「自己寫的不夠好」</strong>、critique 跟 generator 不會共用同樣 blind spot。</li>
<li><strong>失敗在 systematic error</strong>：critique 跟 generator 是同個模型、訓練分佈中的盲點不會因為「再想一次」消失。判讀訊號：critique 每次給很像的建議、或修完還是同一類錯——換 critic 用不同 base model、或加外部驗證（test、lint、schema）取代 LLM critique。</li>
<li><strong>失敗在低能力模型</strong>：critic 能力不足、產不出有用建議、徒增 cost / latency。</li>
<li><strong>失敗在無限循環</strong>：沒有客觀停止訊號、reflection 一直跑、cost 爆掉。緩解：step cap + 外部 metric（test pass、schema valid）。</li>
<li><strong>失敗在過度修正</strong>：每次 reflection 都改一點、累積結果變糟（過度 fitting critic 意見）。緩解：保留 baseline、reflection 結果要跟 baseline 比、不一定採用。</li>
</ol>
<p><a href="/blog/llm/knowledge-cards/agent-loop/" data-link-title="Agent Loop" data-link-desc="LLM agent 自我循環的工作流：LLM 規劃下一步、執行 tool、看結果、再規劃下一步、直到任務完成或停止條件觸發">Agent loop</a> 是 reflection 的延伸特例、進階失敗模式見 <a href="/blog/llm/04-applications/agent-architecture/" data-link-title="4.4 Agent 架構原理" data-link-desc="Agent loop 結構、失敗模式、什麼任務適合 vs 不適合、跟人類審查的協作模型">4.4 Agent 架構</a>。完整 workflow pattern 比較見 <a href="/blog/llm/04-applications/workflow-patterns/" data-link-title="4.7 Workflow 編排模式" data-link-desc="Pipeline / router / parallel / reflection：多 LLM call 組合的四種基本模式與退化條件">4.7 Workflow patterns</a>。</p>
]]></content:encoded></item><item><title>CI/CD 失敗到修復發布流程</title><link>https://tarrragon.github.io/blog/ci/github-actions-failure-flow/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/github-actions-failure-flow/</guid><description>&lt;p>CI/CD 失敗處理的核心責任是把紅燈轉成明確的下一步路由。紅燈本身是驗證或交付層的訊號；工程流程要做的是找出失敗層、重現同一個條件、修正後重新讓 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/ci-pipeline/" data-link-title="CI Pipeline" data-link-desc="說明持續整合如何在合併前自動驗證變更品質與相容性">CI Pipeline&lt;/a> 證明變更可發布。&lt;/p>
&lt;h2 id="失敗後先看什麼">失敗後先看什麼&lt;/h2>
&lt;p>失敗後第一步是定位 workflow 與 job。CI/CD 系統會把一次 push、pull request、tag 或 release 拆成多個 workflow，每個 workflow 下面又有多個 job；真正的下一步取決於是哪一層失敗。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>失敗位置&lt;/th>
 &lt;th>常見原因&lt;/th>
 &lt;th>下一步路由&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Lint / format&lt;/td>
 &lt;td>程式碼、文件或設定格式不符&lt;/td>
 &lt;td>回本機跑同一條 lint / format 命令&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Test&lt;/td>
 &lt;td>單元、整合、瀏覽器或裝置測試回歸&lt;/td>
 &lt;td>下載 report，回本機用同條件重現&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Build&lt;/td>
 &lt;td>編譯、bundle、package 或靜態產物失敗&lt;/td>
 &lt;td>回本機跑 production build 入口&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Package&lt;/td>
 &lt;td>image、app bundle、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">artifact&lt;/a> 產生失敗&lt;/td>
 &lt;td>檢查版本、簽章、registry 或路徑&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Deploy&lt;/td>
 &lt;td>hosting、runtime、store 或權限設定&lt;/td>
 &lt;td>先確認 build &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">artifact&lt;/a> 是否已成功&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Lint / format 失敗代表靜態契約沒有通過。常見情境是程式格式、文件格式、型別檢查、schema 或設定規則不符合規範。這類失敗的修復路徑通常很短：讀錯誤訊息、修正來源、必要時跑 formatter，再提交修正。&lt;/p>
&lt;p>Test 失敗代表某個行為或契約沒有符合預期。這類失敗要先看 report、screenshot、trace、device log 或 error context，確認是功能真的回歸、測試假設過期，還是測試環境缺少 production-like artifact。直接改測試前，要先確認測試原本守的是哪個使用者或系統行為。&lt;/p>
&lt;p>Build 失敗代表 pipeline 尚未產生可部署產物。這類失敗通常來自編譯錯誤、bundle 設定、依賴版本、環境變數、template 或資源路徑。修復時以專案定義的 production build 命令作為最小重現入口。&lt;/p>
&lt;p>Deploy 失敗代表發布動作沒有完成。這類失敗需要先區分 artifact 是否存在、發布通道權限是否正確、環境保護是否放行。若測試與 build 已成功，deploy 失敗多半是發布通道問題；若 artifact 沒有產生，應回到 build 或 package 階段。&lt;/p>
&lt;h2 id="本機重現流程">本機重現流程&lt;/h2>
&lt;p>本機重現的責任是讓修復建立在同一個驗證條件上。CI 是用乾淨環境執行的一組命令；只要能在本機跑出同樣的失敗，修復就能被快速驗證。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">make build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">make &lt;span class="nb">test&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">make deploy-dry-run&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Build 命令驗證 production artifact 是否能產生。這一步應該接近 CI 使用的 build 入口，避免開發模式遮蔽 production 問題。&lt;/p>
&lt;p>Test 命令驗證產物或程式行為。前端可能是 browser test，後端可能是 integration / contract test，App 可能是 device test，Docker 可能是 image scan 或 smoke test。&lt;/p>
&lt;p>Deploy dry-run 命令驗證發布前條件。高風險部署至少要能檢查 artifact、權限、環境與版本資訊；沒有 dry-run 的專案，也應保留對等的 preflight check。&lt;/p>
&lt;h2 id="修復與重新觸發">修復與重新觸發&lt;/h2>
&lt;p>修復流程的核心是用新 commit 讓 CI 重新驗證。一般流程不需要刪掉失敗 commit，也不需要 force push；失敗 commit 留在歷史裡，後續 fix commit 會形成清楚的修復脈絡。&lt;/p>
&lt;ol>
&lt;li>讀失敗 job 的 log 或 artifact。&lt;/li>
&lt;li>在本機跑對應命令重現。&lt;/li>
&lt;li>修改最小必要範圍。&lt;/li>
&lt;li>跑同一條本機命令確認修復。&lt;/li>
&lt;li>commit 並 push。&lt;/li>
&lt;li>等 GitHub Actions 重新跑。&lt;/li>
&lt;/ol>
&lt;p>這個流程的好處是保留可追溯性。日後再看到同類失敗，可以從 commit history 與 CI log 找到當時的判讀方式。&lt;/p>
&lt;h2 id="發布-gate-路由">發布 gate 路由&lt;/h2>
&lt;p>發布 gate 的責任是把「是否進入下一階段」變成明確條件。這一頁只處理失敗後的操作路由；&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/required-checks/" data-link-title="Required Checks" data-link-desc="說明 pull request 的必要檢查如何作為合併 gate">required checks&lt;/a>、job &lt;code>needs&lt;/code>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">environment protection&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">artifact handoff&lt;/a> 的設計原理，獨立放在 &lt;a href="../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>CI/CD 失敗處理的核心責任是把紅燈轉成明確的下一步路由。紅燈本身是驗證或交付層的訊號；工程流程要做的是找出失敗層、重現同一個條件、修正後重新讓 <a href="/blog/ci/knowledge-cards/ci-pipeline/" data-link-title="CI Pipeline" data-link-desc="說明持續整合如何在合併前自動驗證變更品質與相容性">CI Pipeline</a> 證明變更可發布。</p>
<h2 id="失敗後先看什麼">失敗後先看什麼</h2>
<p>失敗後第一步是定位 workflow 與 job。CI/CD 系統會把一次 push、pull request、tag 或 release 拆成多個 workflow，每個 workflow 下面又有多個 job；真正的下一步取決於是哪一層失敗。</p>
<table>
  <thead>
      <tr>
          <th>失敗位置</th>
          <th>常見原因</th>
          <th>下一步路由</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Lint / format</td>
          <td>程式碼、文件或設定格式不符</td>
          <td>回本機跑同一條 lint / format 命令</td>
      </tr>
      <tr>
          <td>Test</td>
          <td>單元、整合、瀏覽器或裝置測試回歸</td>
          <td>下載 report，回本機用同條件重現</td>
      </tr>
      <tr>
          <td>Build</td>
          <td>編譯、bundle、package 或靜態產物失敗</td>
          <td>回本機跑 production build 入口</td>
      </tr>
      <tr>
          <td>Package</td>
          <td>image、app bundle、<a href="/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">artifact</a> 產生失敗</td>
          <td>檢查版本、簽章、registry 或路徑</td>
      </tr>
      <tr>
          <td>Deploy</td>
          <td>hosting、runtime、store 或權限設定</td>
          <td>先確認 build <a href="/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">artifact</a> 是否已成功</td>
      </tr>
  </tbody>
</table>
<p>Lint / format 失敗代表靜態契約沒有通過。常見情境是程式格式、文件格式、型別檢查、schema 或設定規則不符合規範。這類失敗的修復路徑通常很短：讀錯誤訊息、修正來源、必要時跑 formatter，再提交修正。</p>
<p>Test 失敗代表某個行為或契約沒有符合預期。這類失敗要先看 report、screenshot、trace、device log 或 error context，確認是功能真的回歸、測試假設過期，還是測試環境缺少 production-like artifact。直接改測試前，要先確認測試原本守的是哪個使用者或系統行為。</p>
<p>Build 失敗代表 pipeline 尚未產生可部署產物。這類失敗通常來自編譯錯誤、bundle 設定、依賴版本、環境變數、template 或資源路徑。修復時以專案定義的 production build 命令作為最小重現入口。</p>
<p>Deploy 失敗代表發布動作沒有完成。這類失敗需要先區分 artifact 是否存在、發布通道權限是否正確、環境保護是否放行。若測試與 build 已成功，deploy 失敗多半是發布通道問題；若 artifact 沒有產生，應回到 build 或 package 階段。</p>
<h2 id="本機重現流程">本機重現流程</h2>
<p>本機重現的責任是讓修復建立在同一個驗證條件上。CI 是用乾淨環境執行的一組命令；只要能在本機跑出同樣的失敗，修復就能被快速驗證。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">make build
</span></span><span class="line"><span class="ln">2</span><span class="cl">make <span class="nb">test</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">make deploy-dry-run</span></span></code></pre></div><p>Build 命令驗證 production artifact 是否能產生。這一步應該接近 CI 使用的 build 入口，避免開發模式遮蔽 production 問題。</p>
<p>Test 命令驗證產物或程式行為。前端可能是 browser test，後端可能是 integration / contract test，App 可能是 device test，Docker 可能是 image scan 或 smoke test。</p>
<p>Deploy dry-run 命令驗證發布前條件。高風險部署至少要能檢查 artifact、權限、環境與版本資訊；沒有 dry-run 的專案，也應保留對等的 preflight check。</p>
<h2 id="修復與重新觸發">修復與重新觸發</h2>
<p>修復流程的核心是用新 commit 讓 CI 重新驗證。一般流程不需要刪掉失敗 commit，也不需要 force push；失敗 commit 留在歷史裡，後續 fix commit 會形成清楚的修復脈絡。</p>
<ol>
<li>讀失敗 job 的 log 或 artifact。</li>
<li>在本機跑對應命令重現。</li>
<li>修改最小必要範圍。</li>
<li>跑同一條本機命令確認修復。</li>
<li>commit 並 push。</li>
<li>等 GitHub Actions 重新跑。</li>
</ol>
<p>這個流程的好處是保留可追溯性。日後再看到同類失敗，可以從 commit history 與 CI log 找到當時的判讀方式。</p>
<h2 id="發布-gate-路由">發布 gate 路由</h2>
<p>發布 gate 的責任是把「是否進入下一階段」變成明確條件。這一頁只處理失敗後的操作路由；<a href="/blog/ci/knowledge-cards/required-checks/" data-link-title="Required Checks" data-link-desc="說明 pull request 的必要檢查如何作為合併 gate">required checks</a>、job <code>needs</code>、<a href="/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">environment protection</a> 與 <a href="/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">artifact handoff</a> 的設計原理，獨立放在 <a href="../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</p>
<h2 id="常見處理情境">常見處理情境</h2>
<p>CI 失敗但本機通過時，優先檢查環境差異。常見差異包括語言版本、套件管理器版本、缺少子模組、缺少 build artifact、測試依賴未安裝、時區或檔案大小寫差異。這類問題要把版本與建置前置條件寫進 workflow、Makefile 或 script，讓重現條件成為專案的一部分。</p>
<p>測試不穩定時，優先把 <a href="/blog/ci/knowledge-cards/flaky-test/" data-link-title="Flaky Test" data-link-desc="說明非決定性測試如何降低 CI gate 信任度與治理方式">Flaky Test</a> 狀態標出來並建立 owner。短期可以隔離或重跑，長期要找到不穩定來源，例如等待條件錯誤、外部網路依賴、時間假設、測試資料不穩或動畫 transition 尚未完成。測試不穩定會降低 gate 信任度，因此它本身就是需要治理的 CI 問題。</p>
<p>Deploy 失敗但測試通過時，優先看 artifact 與權限。若 build output 存在且可下載，問題通常在部署通道、token permission 或 <a href="/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">environment protection</a>；若 artifact 缺失，就回到 build job。</p>
<h2 id="反模式與替代做法">反模式與替代做法</h2>
<table>
  <thead>
      <tr>
          <th>反模式</th>
          <th>風險</th>
          <th>替代做法</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>看到紅燈直接重跑</td>
          <td>掩蓋 flaky 或環境問題</td>
          <td>先看失敗 log，再決定是否重跑</td>
      </tr>
      <tr>
          <td>用 <code>--no-verify</code> 或跳過 CI</td>
          <td>把局部問題帶進主線</td>
          <td>修掉 gate 或明確記錄例外</td>
      </tr>
      <tr>
          <td>CI 與本機命令不同</td>
          <td>本機通過但 CI 失敗</td>
          <td>把命令收斂到 Makefile / npm script</td>
      </tr>
      <tr>
          <td>測試直接打外部服務</td>
          <td>網路與第三方狀態污染判斷</td>
          <td>使用 fixture、mock 或可控環境</td>
      </tr>
  </tbody>
</table>
<p>反模式的共同問題是讓 CI 失去判讀價值。CI 的目標是讓綠燈代表「這次變更在定義好的條件下可發布」。</p>
<h2 id="最小可用流程">最小可用流程</h2>
<p>最小可用流程是讓每次變更都有同一條路徑。對小型靜態網站或個人 blog，先做到以下四件事，就能形成穩定發布節奏。</p>
<ol>
<li><code>push</code> 或 PR 觸發 lint / test / build。</li>
<li>production build 有單一入口。</li>
<li>測試失敗時保留 artifact 或 report。</li>
<li>deploy 只接受測試與 build 通過後的產物。</li>
</ol>
<p>這套流程建立後，CI 紅燈就會成為清楚的路由訊號：哪一層壞、用哪個命令重現、修完後用哪個 gate 放行。</p>
<p>若變更涉及後端服務，可再對照 backend 知識卡的 <a href="/blog/backend/knowledge-cards/runbook/" data-link-title="Runbook" data-link-desc="說明 runbook 如何把事故判斷與操作步驟標準化">Runbook</a>、<a href="/blog/backend/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明事故期間如何判斷回滾、回切與暫停變更">Rollback Strategy</a> 與 <a href="/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">Release Gate</a> 進一步細化故障處理順序與放行條件。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>需要理解 CI 在可靠性模組的位置：讀 <a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">6.1 CI pipeline</a>。</li>
<li>需要看靜態站部署案例：讀 <a href="../blog-project-deploy/">本 blog 專案部署</a>。</li>
<li>需要理解 CI gate 設計：讀 <a href="../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
<li>需要理解發布阻擋策略：讀 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate 與變更節奏</a>。</li>
</ul>
]]></content:encoded></item><item><title>本 blog 專案的 GitHub Actions workflow</title><link>https://tarrragon.github.io/blog/ci/blog-project-deploy/github-actions-workflows/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/blog-project-deploy/github-actions-workflows/</guid><description>&lt;p>本 blog 的 GitHub Actions workflow 負責把內容檢查、瀏覽器回歸測試、Hugo 發布與 Claude 協作分成不同自動化流程。每條 workflow 都是一個獨立入口；維護時要先分清楚它是在保護內容品質、使用者行為、發布產物，還是協作流程。&lt;/p>
&lt;h2 id="workflow-總覽">Workflow 總覽&lt;/h2>
&lt;p>本專案目前有五條 workflow。三條屬於 CI / CD 主流程，兩條屬於 Claude 協作輔助流程。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Workflow&lt;/th>
 &lt;th>檔案&lt;/th>
 &lt;th>觸發條件&lt;/th>
 &lt;th>核心責任&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>md-check&lt;/code>&lt;/td>
 &lt;td>&lt;code>.github/workflows/md-check.yml&lt;/code>&lt;/td>
 &lt;td>push / pull request 到 &lt;code>main&lt;/code>&lt;/td>
 &lt;td>檢查 content Markdown 契約&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>Playwright tests&lt;/code>&lt;/td>
 &lt;td>&lt;code>.github/workflows/playwright.yml&lt;/code>&lt;/td>
 &lt;td>push / pull request 到 &lt;code>main&lt;/code>&lt;/td>
 &lt;td>驗證瀏覽器層行為與版面回歸&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>Deploy Hugo site to Pages&lt;/code>&lt;/td>
 &lt;td>&lt;code>.github/workflows/deploy.yml&lt;/code>&lt;/td>
 &lt;td>push 到 &lt;code>main&lt;/code>&lt;/td>
 &lt;td>建置 Hugo、產生搜尋索引並部署&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>Claude Code&lt;/code>&lt;/td>
 &lt;td>&lt;code>.github/workflows/claude.yml&lt;/code>&lt;/td>
 &lt;td>issue / comment / review 叫 Claude&lt;/td>
 &lt;td>讓 Claude 讀 issue、PR 與 CI 結果&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>Claude Code Review&lt;/code>&lt;/td>
 &lt;td>&lt;code>.github/workflows/claude-code-review.yml&lt;/code>&lt;/td>
 &lt;td>PR opened / synchronize 等事件&lt;/td>
 &lt;td>對 PR 進行 Claude code review&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這張表的責任是提供入口。看到 GitHub Actions 紅燈時，先對照 workflow 名稱，把失敗歸到內容檢查、瀏覽器測試、部署或協作流程。&lt;/p>
&lt;h2 id="md-check">&lt;code>md-check&lt;/code>&lt;/h2>
&lt;p>&lt;code>md-check&lt;/code> 的責任是讓 &lt;code>content/&lt;/code> 裡的 Markdown 維持同一套結構契約。它會先用 Go build 出 &lt;code>scripts/mdtools&lt;/code>，再依序執行 formatter 檢查、lint 與卡片連結檢查。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">md-check&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">push&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">branches&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">main]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">pull_request&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">branches&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">main]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這條 workflow 的核心步驟是：&lt;/p>
&lt;ol>
&lt;li>&lt;code>actions/checkout@v6&lt;/code>&lt;/li>
&lt;li>&lt;code>actions/setup-go@v6&lt;/code>&lt;/li>
&lt;li>&lt;code>go build -o ../../bin/mdtools&lt;/code>&lt;/li>
&lt;li>&lt;code>./bin/mdtools fmt --check content/&lt;/code>&lt;/li>
&lt;li>&lt;code>./bin/mdtools lint content/&lt;/code>&lt;/li>
&lt;li>&lt;code>./bin/mdtools cards content/&lt;/code>&lt;/li>
&lt;/ol>
&lt;p>&lt;code>md-check&lt;/code> 失敗時，下一步是回本機跑同一組命令。&lt;code>fmt --check&lt;/code> 失敗代表格式可由 &lt;code>fmt --fix&lt;/code> 修正；&lt;code>lint&lt;/code> 失敗代表標題、front matter、URL、code block 等結構契約不符；&lt;code>cards&lt;/code> 失敗代表卡片連結、orphan 或 K4 規則需要修。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">./bin/mdtools fmt --check content/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">./bin/mdtools lint content/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">./bin/mdtools cards content/&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>維護這條 workflow 時，規則來源要和 &lt;a href="https://tarrragon.github.io/blog/posts/blog-markdown-%E5%AF%AB%E4%BD%9C%E8%A6%8F%E7%AF%84%E8%88%87-mdtools-%E6%AA%A2%E6%9F%A5/" data-link-title="Blog Markdown 寫作規範與 mdtools 檢查" data-link-desc="本 blog 的 Markdown 排版規範權威契約。涵蓋 H1 禁用、MD024 siblings_only、反釣魚 TLD 校驗、卡片雙向完整性、front matter schema；改規則時要與 scripts/mdtools 實作同步。">Blog Markdown 寫作規範與 mdtools 檢查&lt;/a> 對齊。改 &lt;code>scripts/mdtools/internal/rules/&lt;/code> 時，也要同步更新規範文章，避免 CI 行為和文件描述分叉。&lt;/p>
&lt;h2 id="playwright-tests">&lt;code>Playwright tests&lt;/code>&lt;/h2>
&lt;p>&lt;code>Playwright tests&lt;/code> 的責任是驗證使用者可見行為。它會先建出完整 Hugo site 與 Pagefind index，再用 Chromium 驗證搜尋、版面與互動。&lt;/p></description><content:encoded><![CDATA[<p>本 blog 的 GitHub Actions workflow 負責把內容檢查、瀏覽器回歸測試、Hugo 發布與 Claude 協作分成不同自動化流程。每條 workflow 都是一個獨立入口；維護時要先分清楚它是在保護內容品質、使用者行為、發布產物，還是協作流程。</p>
<h2 id="workflow-總覽">Workflow 總覽</h2>
<p>本專案目前有五條 workflow。三條屬於 CI / CD 主流程，兩條屬於 Claude 協作輔助流程。</p>
<table>
  <thead>
      <tr>
          <th>Workflow</th>
          <th>檔案</th>
          <th>觸發條件</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>md-check</code></td>
          <td><code>.github/workflows/md-check.yml</code></td>
          <td>push / pull request 到 <code>main</code></td>
          <td>檢查 content Markdown 契約</td>
      </tr>
      <tr>
          <td><code>Playwright tests</code></td>
          <td><code>.github/workflows/playwright.yml</code></td>
          <td>push / pull request 到 <code>main</code></td>
          <td>驗證瀏覽器層行為與版面回歸</td>
      </tr>
      <tr>
          <td><code>Deploy Hugo site to Pages</code></td>
          <td><code>.github/workflows/deploy.yml</code></td>
          <td>push 到 <code>main</code></td>
          <td>建置 Hugo、產生搜尋索引並部署</td>
      </tr>
      <tr>
          <td><code>Claude Code</code></td>
          <td><code>.github/workflows/claude.yml</code></td>
          <td>issue / comment / review 叫 Claude</td>
          <td>讓 Claude 讀 issue、PR 與 CI 結果</td>
      </tr>
      <tr>
          <td><code>Claude Code Review</code></td>
          <td><code>.github/workflows/claude-code-review.yml</code></td>
          <td>PR opened / synchronize 等事件</td>
          <td>對 PR 進行 Claude code review</td>
      </tr>
  </tbody>
</table>
<p>這張表的責任是提供入口。看到 GitHub Actions 紅燈時，先對照 workflow 名稱，把失敗歸到內容檢查、瀏覽器測試、部署或協作流程。</p>
<h2 id="md-check"><code>md-check</code></h2>
<p><code>md-check</code> 的責任是讓 <code>content/</code> 裡的 Markdown 維持同一套結構契約。它會先用 Go build 出 <code>scripts/mdtools</code>，再依序執行 formatter 檢查、lint 與卡片連結檢查。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">md-check</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">  </span><span class="nt">push</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">main]</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">  </span><span class="nt">pull_request</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">main]</span></span></span></code></pre></div><p>這條 workflow 的核心步驟是：</p>
<ol>
<li><code>actions/checkout@v6</code></li>
<li><code>actions/setup-go@v6</code></li>
<li><code>go build -o ../../bin/mdtools</code></li>
<li><code>./bin/mdtools fmt --check content/</code></li>
<li><code>./bin/mdtools lint content/</code></li>
<li><code>./bin/mdtools cards content/</code></li>
</ol>
<p><code>md-check</code> 失敗時，下一步是回本機跑同一組命令。<code>fmt --check</code> 失敗代表格式可由 <code>fmt --fix</code> 修正；<code>lint</code> 失敗代表標題、front matter、URL、code block 等結構契約不符；<code>cards</code> 失敗代表卡片連結、orphan 或 K4 規則需要修。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">./bin/mdtools fmt --check content/
</span></span><span class="line"><span class="ln">2</span><span class="cl">./bin/mdtools lint content/
</span></span><span class="line"><span class="ln">3</span><span class="cl">./bin/mdtools cards content/</span></span></code></pre></div><p>維護這條 workflow 時，規則來源要和 <a href="/blog/posts/blog-markdown-%E5%AF%AB%E4%BD%9C%E8%A6%8F%E7%AF%84%E8%88%87-mdtools-%E6%AA%A2%E6%9F%A5/" data-link-title="Blog Markdown 寫作規範與 mdtools 檢查" data-link-desc="本 blog 的 Markdown 排版規範權威契約。涵蓋 H1 禁用、MD024 siblings_only、反釣魚 TLD 校驗、卡片雙向完整性、front matter schema；改規則時要與 scripts/mdtools 實作同步。">Blog Markdown 寫作規範與 mdtools 檢查</a> 對齊。改 <code>scripts/mdtools/internal/rules/</code> 時，也要同步更新規範文章，避免 CI 行為和文件描述分叉。</p>
<h2 id="playwright-tests"><code>Playwright tests</code></h2>
<p><code>Playwright tests</code> 的責任是驗證使用者可見行為。它會先建出完整 Hugo site 與 Pagefind index，再用 Chromium 驗證搜尋、版面與互動。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Playwright tests</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">  </span><span class="nt">push</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">main]</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">  </span><span class="nt">pull_request</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">main]</span></span></span></code></pre></div><p>這條 workflow 的核心步驟是：</p>
<ol>
<li>checkout，並包含 submodules</li>
<li>安裝 Hugo <code>0.148.2</code> extended</li>
<li>安裝 Node <code>24</code></li>
<li><code>npm ci</code></li>
<li><code>npx playwright install --with-deps chromium</code></li>
<li><code>make site</code></li>
<li><code>npx playwright test</code></li>
<li>失敗時上傳 <code>playwright-report/</code></li>
</ol>
<p><code>make site</code> 是這條 workflow 的關鍵前置條件。它會產生 Hugo 靜態檔與三份 Pagefind index：<code>pagefind</code>、<code>pagefind-title</code>、<code>pagefind-content</code>。如果只跑 <code>hugo --minify</code> 就跑 Playwright，搜尋測試會因為缺少 index 而失敗。</p>
<p>Playwright 失敗時，下一步是下載 <code>playwright-report</code> 或讀 error context。若失敗發生在搜尋頁，先確認 <code>make site</code> 是否完整成功；若失敗發生在版面，先看 screenshot、bounding box 或 computed style；若失敗發生在互動，先看 selector 是否仍對準真實 DOM。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">make site
</span></span><span class="line"><span class="ln">2</span><span class="cl">npm test</span></span></code></pre></div><p>維護這條 workflow 時，測試要守使用者行為，不應只守 implementation detail。像 TOC RWD 這類版面行為，可以用 viewport 測試固定桌面、筆電與手機三種狀態。</p>
<h2 id="deploy-hugo-site-to-pages"><code>Deploy Hugo site to Pages</code></h2>
<p><code>Deploy Hugo site to Pages</code> 的責任是把 <code>main</code> 上的內容建置成 GitHub Pages artifact 並部署。它只在 push 到 <code>main</code> 時觸發，不在 pull request 上部署。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Deploy Hugo site to Pages</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">  </span><span class="nt">push</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">      </span>- <span class="l">main</span></span></span></code></pre></div><p>這條 workflow 有兩個 job：</p>
<table>
  <thead>
      <tr>
          <th>Job</th>
          <th>責任</th>
          <th>關鍵設定</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>build</code></td>
          <td>checkout、Hugo build、Pagefind、artifact</td>
          <td><code>runs-on: ubuntu-latest</code></td>
      </tr>
      <tr>
          <td><code>deploy</code></td>
          <td>發布 GitHub Pages</td>
          <td><code>needs: build</code></td>
      </tr>
  </tbody>
</table>
<p><code>build</code> job 會先跑 <code>hugo --minify</code>，並把輸出寫到 <code>hugo-build-output.txt</code>。目前它設了 <code>continue-on-error: true</code>，所以 Hugo build 失敗時會進入 Claude Debug 步驟，嘗試讓 Claude 分析錯誤並 commit 修復。</p>
<p><code>Fail if build was not fixed</code> 是第二道保護。若原本 Hugo build 失敗，workflow 會重新跑一次 <code>hugo --minify</code>；如果 Claude 沒修好，這一步會讓 workflow 停止。</p>
<p>Pagefind index 會在 Hugo build 後產生：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">npx -y pagefind --site public --root-selector main
</span></span><span class="line"><span class="ln">2</span><span class="cl">npx -y pagefind --site public --root-selector <span class="s2">&#34;article.article-content &gt; h1&#34;</span> --output-subdir pagefind-title
</span></span><span class="line"><span class="ln">3</span><span class="cl">npx -y pagefind --site public --root-selector <span class="s2">&#34;.article-body&#34;</span> --output-subdir pagefind-content</span></span></code></pre></div><p>Deploy 失敗時，下一步先分層判讀。若 <code>build</code> job 失敗，回到 Hugo 或 Pagefind；若 <code>Upload artifact</code> 成功但 <code>deploy</code> job 失敗，檢查 Pages environment、permission、artifact 與 GitHub Pages 設定。</p>
<p>這條 workflow 目前的注意事項是：deploy workflow 自己沒有直接 <code>needs</code> <code>md-check</code> 或 <code>Playwright tests</code>，因為它們是獨立 workflow。這是本專案目前的實際邊界；gate 設計原理見 <a href="../../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</p>
<h2 id="claude-code"><code>Claude Code</code></h2>
<p><code>Claude Code</code> 的責任是提供互動式 Claude 協作入口。它不會在每次 push 自動修程式，而是在 issue、comment 或 review 內容包含 <code>@claude</code> 時觸發。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">  </span><span class="nt">issue_comment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="nt">types</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">created]</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">  </span><span class="nt">pull_request_review_comment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">    </span><span class="nt">types</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">created]</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">  </span><span class="nt">issues</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="w">    </span><span class="nt">types</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">opened, assigned]</span><span class="w">
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="w">  </span><span class="nt">pull_request_review</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="w">    </span><span class="nt">types</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">submitted]</span></span></span></code></pre></div><p>這條 workflow 的 gate 寫在 job <code>if</code>。只有以下情境會真正執行：</p>
<ul>
<li>issue comment 包含 <code>@claude</code></li>
<li>pull request review comment 包含 <code>@claude</code></li>
<li>pull request review body 包含 <code>@claude</code></li>
<li>issue title 或 body 包含 <code>@claude</code></li>
</ul>
<p>這條 workflow 給 Claude <code>actions: read</code> 權限，讓它能讀 PR 上的 CI 結果。這對「請 Claude 看 CI 為什麼失敗」很重要，因為 Claude 需要讀 workflow run、job log 或 check 結果才能判斷。</p>
<p>維護這條 workflow 時，重點是權限最小化。它目前給的是 <code>contents: read</code>、<code>pull-requests: read</code>、<code>issues: read</code>、<code>id-token: write</code>、<code>actions: read</code>，適合互動分析；若未來要讓 Claude 直接 commit，才需要重新評估寫入權限與保護條件。</p>
<h2 id="claude-code-review"><code>Claude Code Review</code></h2>
<p><code>Claude Code Review</code> 的責任是在 PR 事件發生時跑 Claude code review。它和 <code>Claude Code</code> 不同，前者是 PR review automation，後者是被 <code>@claude</code> 叫起來的互動入口。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">  </span><span class="nt">pull_request</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="nt">types</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">opened, synchronize, ready_for_review, reopened]</span></span></span></code></pre></div><p>這條 workflow 使用 <code>code-review@claude-code-plugins</code>，prompt 是：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}</span></span></code></pre></div><p>它的責任是提供 review 視角。Claude review 可以指出風險、邏輯問題或測試缺口；真正阻擋合併與發布的責任仍在 <a href="/blog/ci/knowledge-cards/required-checks/" data-link-title="Required Checks" data-link-desc="說明 pull request 的必要檢查如何作為合併 gate">Required Checks</a>、測試 workflow 與 deploy gate。</p>
<p>維護這條 workflow 時，可以依 PR 類型決定是否加 path filter。若未來只想在程式碼或 workflow 變更時觸發，可打開 <code>paths</code> 設定；若希望文章內容也被 review，就維持目前全 PR 觸發。</p>
<h2 id="本專案的發布阻擋邊界">本專案的發布阻擋邊界</h2>
<p>本 blog 的發布阻擋邊界需要同時看 YAML 與 GitHub repository 設定。這一節只記錄本專案目前能從 YAML 判讀出的事實；required checks、environment protection 與 artifact handoff 的原理不在本頁展開。</p>
<p>目前從 YAML 可直接確認的阻擋關係是：</p>
<table>
  <thead>
      <tr>
          <th>關係</th>
          <th>是否在 YAML 中明確存在</th>
          <th>說明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>deploy</code> 等 <code>build</code></td>
          <td>是</td>
          <td><code>deploy</code> job 有 <code>needs: build</code></td>
      </tr>
      <tr>
          <td><code>deploy</code> 等 <code>md-check</code></td>
          <td>否</td>
          <td><code>md-check</code> 是另一條 workflow</td>
      </tr>
      <tr>
          <td><code>deploy</code> 等 Playwright</td>
          <td>否</td>
          <td><code>Playwright tests</code> 是另一條 workflow</td>
      </tr>
      <tr>
          <td>PR 需要通過測試才能合併</td>
          <td>需查 repository 設定</td>
          <td>需要看 GitHub branch protection 設定</td>
      </tr>
      <tr>
          <td>Pages deploy 需要人工審核</td>
          <td>需查 environment 設定</td>
          <td>需要看 GitHub Pages environment protection 設定</td>
      </tr>
  </tbody>
</table>
<p>若日後發現測試紅燈但 Pages 仍發布，本頁只負責指出目前 workflow 邊界；具體改法回到 <a href="../../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a> 判斷，並對照 <a href="/blog/ci/knowledge-cards/required-checks/" data-link-title="Required Checks" data-link-desc="說明 pull request 的必要檢查如何作為合併 gate">Required Checks</a> 與 <a href="/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">Environment Protection</a>。</p>
<h2 id="失敗時的維護路由">失敗時的維護路由</h2>
<p>失敗時的維護路由要先定位 workflow，再定位 job，再回到本機重現。這能避免在錯誤層修錯問題。</p>
<table>
  <thead>
      <tr>
          <th>紅燈位置</th>
          <th>優先看什麼</th>
          <th>本機重現命令</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>md-check</code></td>
          <td>mdtools 訊息</td>
          <td><code>./bin/mdtools lint content/</code></td>
      </tr>
      <tr>
          <td><code>Playwright tests</code></td>
          <td><code>playwright-report</code> / error context</td>
          <td><code>make site</code> 後 <code>npm test</code></td>
      </tr>
      <tr>
          <td><code>Deploy</code> 的 Hugo build</td>
          <td><code>hugo-build-output.txt</code></td>
          <td><code>hugo --minify</code></td>
      </tr>
      <tr>
          <td><code>Deploy</code> 的 Pagefind</td>
          <td>Pagefind command output</td>
          <td><code>make site</code></td>
      </tr>
      <tr>
          <td><code>Deploy</code> 的 Pages step</td>
          <td>artifact / permission / environment</td>
          <td>GitHub Actions UI + Pages 設定</td>
      </tr>
      <tr>
          <td><code>Claude Code</code></td>
          <td>secret / permission / trigger <code>if</code></td>
          <td>檢查 <code>@claude</code> 觸發文字與 secrets</td>
      </tr>
      <tr>
          <td><code>Claude Code Review</code></td>
          <td>plugin marketplace / token</td>
          <td>檢查 PR event、secret 與 action log</td>
      </tr>
  </tbody>
</table>
<p>這份路由也可以當維護 checklist。新增 workflow 時，至少要補三件事：觸發條件、失敗時看哪個 artifact 或 log、本機要用哪條命令重現。</p>
<h2 id="本專案維護注意事項">本專案維護注意事項</h2>
<p>本專案維護注意事項的責任是記錄和目前 YAML 直接相關的操作提醒。這些提醒隨 workflow 實作改變而更新，不承擔通用 CI 設計原理。</p>
<ul>
<li><code>Playwright tests</code> 依賴 <code>make site</code> 產生 Pagefind index；搜尋測試失敗時先確認 production build 是否完整。</li>
<li><code>deploy.yml</code> 的 Hugo build 使用 <code>continue-on-error: true</code>，後面用 Claude Debug 與 retry build 接住失敗。</li>
<li><code>Claude Code</code> 目前是 read-oriented 互動入口；若未來要寫入 repo，需要重新審核 permission。</li>
<li><code>.github/workflows/*.yml</code> 有實作變更時，要同步更新本頁，讓維護入口維持可信。</li>
</ul>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>CI 紅燈處理流程：讀 <a href="../../github-actions-failure-flow/">CI 失敗到修復發布流程</a>。</li>
<li>CI gate 設計原理：讀 <a href="../../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
<li>CI 在可靠性模組的位置：讀 <a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">6.1 CI pipeline</a>。</li>
<li>發布 gate 設計：讀 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate 與變更節奏</a>。</li>
<li>Markdown 檢查規則：讀 <a href="/blog/posts/blog-markdown-%E5%AF%AB%E4%BD%9C%E8%A6%8F%E7%AF%84%E8%88%87-mdtools-%E6%AA%A2%E6%9F%A5/" data-link-title="Blog Markdown 寫作規範與 mdtools 檢查" data-link-desc="本 blog 的 Markdown 排版規範權威契約。涵蓋 H1 禁用、MD024 siblings_only、反釣魚 TLD 校驗、卡片雙向完整性、front matter schema；改規則時要與 scripts/mdtools 實作同步。">Blog Markdown 寫作規範與 mdtools 檢查</a>。</li>
</ul>
]]></content:encoded></item><item><title>Dotfile 跟 Infra IaC 的平行關係</title><link>https://tarrragon.github.io/blog/linux/dotfile/00-dotfile-mindset/dotfile-iac-parallel/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/00-dotfile-mindset/dotfile-iac-parallel/</guid><description>&lt;p>&lt;a href="https://tarrragon.github.io/blog/infra/" data-link-title="Infra 基礎設施建置指南" data-link-desc="從零循序漸進把雲端基礎設施做起來 — IaC、身分憑證、網路地基、環境分離、核心服務、可觀測性、自動化 review 與治理習慣，含怎麼在組織內推動">Infra 基礎設施建置指南&lt;/a>教的是用 Terraform 或 OpenTofu 把雲端資源（VPC、IAM role、EC2 instance）寫成代碼，讓基礎設施可重現、可 review、可回滾。Dotfile 做的事在概念上完全平行：把個人工作環境（shell、editor、terminal、window manager）寫成代碼，達成同樣的可重現性。&lt;/p>
&lt;h2 id="共用的核心原則">共用的核心原則&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>宣告式&lt;/strong>：描述「環境應該長什麼樣」，而非「操作了哪些步驟」。Terraform 宣告「要有一個 VPC、CIDR 是 10.0.0.0/16」；dotfile 宣告「zsh 的 prompt 格式是這樣、alias ll 對應 ls -la」。&lt;/li>
&lt;li>&lt;strong>版控下的變更歷史&lt;/strong>：誰改了什麼、什麼時候改的、為什麼改，都在 Git log 裡。環境出問題時可以回溯到「上一次正常的狀態」是哪個 commit。&lt;/li>
&lt;li>&lt;strong>可 review&lt;/strong>：改了一個 shell function，diff 清楚可讀。跟在 terminal 裡直接 export 一個變數、下次重開就忘了相比，版控下的改動有跡可循。&lt;/li>
&lt;/ul>
&lt;h2 id="差異">差異&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>維度&lt;/th>
 &lt;th>Infra IaC&lt;/th>
 &lt;th>Dotfile&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>管理對象&lt;/td>
 &lt;td>組織的雲端資源&lt;/td>
 &lt;td>個人的工作桌面&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>State 管理&lt;/td>
 &lt;td>Remote backend + lock 機制（防並行衝突）&lt;/td>
 &lt;td>通常只用 Git，沒有額外 state file&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>生效方式&lt;/td>
 &lt;td>&lt;code>terraform plan&lt;/code> → &lt;code>terraform apply&lt;/code> 兩步&lt;/td>
 &lt;td>多數改完 source 即生效，或重開 terminal 生效&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>影響範圍&lt;/td>
 &lt;td>改錯可能影響 production 服務&lt;/td>
 &lt;td>改錯最多影響自己的工作環境&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>協作需求&lt;/td>
 &lt;td>團隊共用、需要 PR review&lt;/td>
 &lt;td>通常個人維護，PR review 是可選的&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這個平行不只是比喻。&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/09-team-environment/" data-link-title="模組九：從個人到團隊" data-link-desc="個人 dotfile 管理的思想要延伸到團隊開發環境標準化時回來讀 — devcontainer、nix、商業環境配置管理">從個人到團隊&lt;/a>會教怎麼把 dotfile 的思想正式擴展到團隊環境——devcontainer 把「開發環境應該長什麼樣」寫成宣告式配置，讓新人 clone repo 就能拿到一致的開發環境，這正是 IaC 思想從組織 infra 往個人工作桌面延伸的具體產物。&lt;/p>
&lt;h2 id="dotfile-是重建指令不是備份">Dotfile 是重建指令，不是備份&lt;/h2>
&lt;p>這是最重要的心智模型區分。Dotfile repo 的目標不是「把舊電腦的所有檔案搬到新電腦」（那是備份工具的工作），而是「一份能在空白機器上重建工作環境的指令集」。&lt;/p>
&lt;p>這個思維跟 Docker 的哲學一致：Docker image 透過 Dockerfile「描述如何重建」環境，而不是「對一台跑著的伺服器拍快照」。Dotfile repo 也是——它記錄的是「你的環境應該長什麼樣」的宣告，不是「你的機器上現在有什麼」的快照。&lt;/p>
&lt;p>這個區分決定了 repo 裡該放什麼：&lt;/p>
&lt;ul>
&lt;li>放進去的：&lt;strong>宣告式的配置檔&lt;/strong>（shell config、editor config、WM config）、&lt;strong>套件清單&lt;/strong>（Brewfile、pacman list）、&lt;strong>安裝腳本&lt;/strong>（&lt;code>install.sh&lt;/code>，用來在新機器上自動化部署流程）。&lt;/li>
&lt;li>不放的：&lt;strong>暫存狀態&lt;/strong>（shell history、undo file、session file）、&lt;strong>generated 產物&lt;/strong>（plugin 的 compiled cache）、&lt;strong>大型二進位檔&lt;/strong>（字型檔案可以用套件管理器裝，不用放 repo）。&lt;/li>
&lt;/ul>
&lt;p>維持「重建指令」的純度，repo 才能保持輕量、diff 可讀、跨機器部署不會帶進不必要的狀態。&lt;/p></description><content:encoded><![CDATA[<p><a href="/blog/infra/" data-link-title="Infra 基礎設施建置指南" data-link-desc="從零循序漸進把雲端基礎設施做起來 — IaC、身分憑證、網路地基、環境分離、核心服務、可觀測性、自動化 review 與治理習慣，含怎麼在組織內推動">Infra 基礎設施建置指南</a>教的是用 Terraform 或 OpenTofu 把雲端資源（VPC、IAM role、EC2 instance）寫成代碼，讓基礎設施可重現、可 review、可回滾。Dotfile 做的事在概念上完全平行：把個人工作環境（shell、editor、terminal、window manager）寫成代碼，達成同樣的可重現性。</p>
<h2 id="共用的核心原則">共用的核心原則</h2>
<ul>
<li><strong>宣告式</strong>：描述「環境應該長什麼樣」，而非「操作了哪些步驟」。Terraform 宣告「要有一個 VPC、CIDR 是 10.0.0.0/16」；dotfile 宣告「zsh 的 prompt 格式是這樣、alias ll 對應 ls -la」。</li>
<li><strong>版控下的變更歷史</strong>：誰改了什麼、什麼時候改的、為什麼改，都在 Git log 裡。環境出問題時可以回溯到「上一次正常的狀態」是哪個 commit。</li>
<li><strong>可 review</strong>：改了一個 shell function，diff 清楚可讀。跟在 terminal 裡直接 export 一個變數、下次重開就忘了相比，版控下的改動有跡可循。</li>
</ul>
<h2 id="差異">差異</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>Infra IaC</th>
          <th>Dotfile</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>管理對象</td>
          <td>組織的雲端資源</td>
          <td>個人的工作桌面</td>
      </tr>
      <tr>
          <td>State 管理</td>
          <td>Remote backend + lock 機制（防並行衝突）</td>
          <td>通常只用 Git，沒有額外 state file</td>
      </tr>
      <tr>
          <td>生效方式</td>
          <td><code>terraform plan</code> → <code>terraform apply</code> 兩步</td>
          <td>多數改完 source 即生效，或重開 terminal 生效</td>
      </tr>
      <tr>
          <td>影響範圍</td>
          <td>改錯可能影響 production 服務</td>
          <td>改錯最多影響自己的工作環境</td>
      </tr>
      <tr>
          <td>協作需求</td>
          <td>團隊共用、需要 PR review</td>
          <td>通常個人維護，PR review 是可選的</td>
      </tr>
  </tbody>
</table>
<p>這個平行不只是比喻。<a href="/blog/linux/dotfile/09-team-environment/" data-link-title="模組九：從個人到團隊" data-link-desc="個人 dotfile 管理的思想要延伸到團隊開發環境標準化時回來讀 — devcontainer、nix、商業環境配置管理">從個人到團隊</a>會教怎麼把 dotfile 的思想正式擴展到團隊環境——devcontainer 把「開發環境應該長什麼樣」寫成宣告式配置，讓新人 clone repo 就能拿到一致的開發環境，這正是 IaC 思想從組織 infra 往個人工作桌面延伸的具體產物。</p>
<h2 id="dotfile-是重建指令不是備份">Dotfile 是重建指令，不是備份</h2>
<p>這是最重要的心智模型區分。Dotfile repo 的目標不是「把舊電腦的所有檔案搬到新電腦」（那是備份工具的工作），而是「一份能在空白機器上重建工作環境的指令集」。</p>
<p>這個思維跟 Docker 的哲學一致：Docker image 透過 Dockerfile「描述如何重建」環境，而不是「對一台跑著的伺服器拍快照」。Dotfile repo 也是——它記錄的是「你的環境應該長什麼樣」的宣告，不是「你的機器上現在有什麼」的快照。</p>
<p>這個區分決定了 repo 裡該放什麼：</p>
<ul>
<li>放進去的：<strong>宣告式的配置檔</strong>（shell config、editor config、WM config）、<strong>套件清單</strong>（Brewfile、pacman list）、<strong>安裝腳本</strong>（<code>install.sh</code>，用來在新機器上自動化部署流程）。</li>
<li>不放的：<strong>暫存狀態</strong>（shell history、undo file、session file）、<strong>generated 產物</strong>（plugin 的 compiled cache）、<strong>大型二進位檔</strong>（字型檔案可以用套件管理器裝，不用放 repo）。</li>
</ul>
<p>維持「重建指令」的純度，repo 才能保持輕量、diff 可讀、跨機器部署不會帶進不必要的狀態。</p>
]]></content:encoded></item><item><title>環境建置的操作順序</title><link>https://tarrragon.github.io/blog/linux/dotfile/00-dotfile-mindset/setup-order-guide/</link><pubDate>Tue, 30 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/00-dotfile-mindset/setup-order-guide/</guid><description>&lt;p>Dotfile 教學模組按主題組織（shell、終端機、視窗管理），適合理解各層概念。但第一次建環境時需要的是另一種順序——&lt;strong>按依賴關係排列的操作清單&lt;/strong>，因為有些步驟是後續步驟的前提。&lt;/p>
&lt;p>SSH key 是典型例子：管理工具和 shell 配置的知識在模組一和模組二，但實際操作時 SSH key 比這兩者都早——因為 &lt;code>git clone git@github.com:...&lt;/code> 本身就需要 SSH key。如果照模組順序走，到模組二才發現 dotfile repo clone 不下來。&lt;/p>
&lt;p>這篇是路線圖，告訴你每一步做什麼、為什麼這個順序、以及去哪個模組看具體操作。&lt;/p>
&lt;h2 id="階段一基礎設施後續所有步驟的前提">階段一：基礎設施（後續所有步驟的前提）&lt;/h2>
&lt;p>這些步驟在任何配置之前完成，因為它們是 Git、遠端存取、dotfile clone 的前提。&lt;/p>
&lt;h3 id="1-安裝作業系統--建立使用者帳號">1. 安裝作業系統 + 建立使用者帳號&lt;/h3>
&lt;p>macOS：開箱即用。Linux：選發行版（Arch 如果要用 Hyprland）、完成安裝、建立非 root 使用者。安裝程式每個選項該怎麼判讀、裝完最小系統缺哪些必要工具（連 &lt;code>sudo&lt;/code> 都可能沒有），見 &lt;a href="https://tarrragon.github.io/blog/linux/install/install-option-decisions/" data-link-title="Linux 安裝選項判讀" data-link-desc="在 Linux 安裝程式面對 locale、網路、磁碟分割、檔案系統、bootloader 等選項、需要判斷依據而非靠預設值硬選時回來讀">Linux 安裝選項判讀&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/linux/install/minimal-install-verify/" data-link-title="最小安裝後的工具驗證與補足" data-link-desc="最小化安裝的 Linux 裝完發現連 sudo 或 which 都沒有、bootstrap 腳本第一行就炸、需要先確認系統缺哪些必要工具再補時回來讀">最小安裝後的工具驗證與補足&lt;/a>——這一步是整個系列展開最深、卻最常被一句帶過的地基。&lt;/p>
&lt;h3 id="2-生成-ssh-key-pair">2. 生成 SSH key pair&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">ssh-keygen -t ed25519 -C &lt;span class="s2">&amp;#34;your-email@example.com&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>為什麼這麼早做：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Git 操作&lt;/strong>：GitHub / GitLab 的 SSH 認證需要 public key。dotfile repo 通常用 SSH URL（&lt;code>git@github.com:...&lt;/code>），clone 前要先把 key 部署到 GitHub。用 HTTPS URL 可以繞過 SSH key，但長期來看 SSH key 是更省事的認證方式。還沒有 key、或想用 HTTPS / PAT 把 dotfile 弄進一台新機器的幾種路徑，見 &lt;a href="https://tarrragon.github.io/blog/linux/install/ssh-keyless-bootstrap/" data-link-title="外部連入、SSH key 與無 key 的 bootstrap 路徑" data-link-desc="要從本機終端機操作新裝好的 Linux 機器、設 SSH key 免密碼、或還沒有 key 就想把 dotfile 弄進機器跑 install.sh 時回來讀">外部連入、SSH key 與無 key 的 bootstrap 路徑&lt;/a>。&lt;/li>
&lt;li>&lt;strong>遠端救援&lt;/strong>：&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/07-desktop-maintenance/" data-link-title="模組七：桌面環境維護與故障排除" data-link-desc="桌面凍結、compositor 掛了、或某個工具不回應時回來讀 — Linux 桌面的故障隔離模型、常見故障場景的恢復操作、日誌判讀與診斷工具">模組七&lt;/a>的場景三（GPU hang）依賴 SSH 作為桌面凍結時的救生通道。key 提前設好，出問題時才有路可走。&lt;/li>
&lt;li>&lt;strong>跨機器操作&lt;/strong>：筆電連桌機、桌機連 VM、VS Code Remote SSH——都靠這把 key。&lt;/li>
&lt;/ul>
&lt;h3 id="3-部署-public-key">3. 部署 public key&lt;/h3>
&lt;p>把 &lt;code>~/.ssh/id_ed25519.pub&lt;/code> 加到需要的服務：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 加到 GitHub&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">cat ~/.ssh/id_ed25519.pub
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="c1"># 複製輸出，貼到 GitHub → Settings → SSH and GPG keys → New SSH key&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># 加到另一台機器（可選，用於跨機器 SSH）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">ssh-copy-id user@target-machine&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="4-package-manager--git">4. Package manager + Git&lt;/h3>
&lt;p>macOS：先裝 Homebrew（macOS 的套件管理器，後續安裝 stow、tmux 等工具都靠它），再裝 Git：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 安裝 Homebrew&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">/bin/bash -c &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 安裝 Git（或用 xcode-select --install，會一併裝 Apple 的 Git）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">brew install git&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Arch：&lt;code>pacman&lt;/code> 隨 OS 安裝已可用，直接裝 Git：&lt;code>pacman -S git&lt;/code>。&lt;/p></description><content:encoded><![CDATA[<p>Dotfile 教學模組按主題組織（shell、終端機、視窗管理），適合理解各層概念。但第一次建環境時需要的是另一種順序——<strong>按依賴關係排列的操作清單</strong>，因為有些步驟是後續步驟的前提。</p>
<p>SSH key 是典型例子：管理工具和 shell 配置的知識在模組一和模組二，但實際操作時 SSH key 比這兩者都早——因為 <code>git clone git@github.com:...</code> 本身就需要 SSH key。如果照模組順序走，到模組二才發現 dotfile repo clone 不下來。</p>
<p>這篇是路線圖，告訴你每一步做什麼、為什麼這個順序、以及去哪個模組看具體操作。</p>
<h2 id="階段一基礎設施後續所有步驟的前提">階段一：基礎設施（後續所有步驟的前提）</h2>
<p>這些步驟在任何配置之前完成，因為它們是 Git、遠端存取、dotfile clone 的前提。</p>
<h3 id="1-安裝作業系統--建立使用者帳號">1. 安裝作業系統 + 建立使用者帳號</h3>
<p>macOS：開箱即用。Linux：選發行版（Arch 如果要用 Hyprland）、完成安裝、建立非 root 使用者。安裝程式每個選項該怎麼判讀、裝完最小系統缺哪些必要工具（連 <code>sudo</code> 都可能沒有），見 <a href="/blog/linux/install/install-option-decisions/" data-link-title="Linux 安裝選項判讀" data-link-desc="在 Linux 安裝程式面對 locale、網路、磁碟分割、檔案系統、bootloader 等選項、需要判斷依據而非靠預設值硬選時回來讀">Linux 安裝選項判讀</a> 與 <a href="/blog/linux/install/minimal-install-verify/" data-link-title="最小安裝後的工具驗證與補足" data-link-desc="最小化安裝的 Linux 裝完發現連 sudo 或 which 都沒有、bootstrap 腳本第一行就炸、需要先確認系統缺哪些必要工具再補時回來讀">最小安裝後的工具驗證與補足</a>——這一步是整個系列展開最深、卻最常被一句帶過的地基。</p>
<h3 id="2-生成-ssh-key-pair">2. 生成 SSH key pair</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">ssh-keygen -t ed25519 -C <span class="s2">&#34;your-email@example.com&#34;</span></span></span></code></pre></div><p>為什麼這麼早做：</p>
<ul>
<li><strong>Git 操作</strong>：GitHub / GitLab 的 SSH 認證需要 public key。dotfile repo 通常用 SSH URL（<code>git@github.com:...</code>），clone 前要先把 key 部署到 GitHub。用 HTTPS URL 可以繞過 SSH key，但長期來看 SSH key 是更省事的認證方式。還沒有 key、或想用 HTTPS / PAT 把 dotfile 弄進一台新機器的幾種路徑，見 <a href="/blog/linux/install/ssh-keyless-bootstrap/" data-link-title="外部連入、SSH key 與無 key 的 bootstrap 路徑" data-link-desc="要從本機終端機操作新裝好的 Linux 機器、設 SSH key 免密碼、或還沒有 key 就想把 dotfile 弄進機器跑 install.sh 時回來讀">外部連入、SSH key 與無 key 的 bootstrap 路徑</a>。</li>
<li><strong>遠端救援</strong>：<a href="/blog/linux/dotfile/07-desktop-maintenance/" data-link-title="模組七：桌面環境維護與故障排除" data-link-desc="桌面凍結、compositor 掛了、或某個工具不回應時回來讀 — Linux 桌面的故障隔離模型、常見故障場景的恢復操作、日誌判讀與診斷工具">模組七</a>的場景三（GPU hang）依賴 SSH 作為桌面凍結時的救生通道。key 提前設好，出問題時才有路可走。</li>
<li><strong>跨機器操作</strong>：筆電連桌機、桌機連 VM、VS Code Remote SSH——都靠這把 key。</li>
</ul>
<h3 id="3-部署-public-key">3. 部署 public key</h3>
<p>把 <code>~/.ssh/id_ed25519.pub</code> 加到需要的服務：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 加到 GitHub</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">cat ~/.ssh/id_ed25519.pub
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># 複製輸出，貼到 GitHub → Settings → SSH and GPG keys → New SSH key</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># 加到另一台機器（可選，用於跨機器 SSH）</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">ssh-copy-id user@target-machine</span></span></code></pre></div><h3 id="4-package-manager--git">4. Package manager + Git</h3>
<p>macOS：先裝 Homebrew（macOS 的套件管理器，後續安裝 stow、tmux 等工具都靠它），再裝 Git：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 安裝 Homebrew</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">/bin/bash -c <span class="s2">&#34;</span><span class="k">$(</span>curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 安裝 Git（或用 xcode-select --install，會一併裝 Apple 的 Git）</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">brew install git</span></span></code></pre></div><p>Arch：<code>pacman</code> 隨 OS 安裝已可用，直接裝 Git：<code>pacman -S git</code>。</p>
<h3 id="5-clone-dotfile-repo">5. Clone dotfile repo</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">git clone git@github.com:yourname/dotfiles.git ~/dotfiles</span></span></code></pre></div><p>如果是第一次建 dotfile repo（還沒有 repo），先建一個空的再開始往裡面加配置。具體做法見<a href="/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">模組一：管理工具與目錄結構</a>。</p>
<h2 id="階段二shell-與終端機日常操作的基礎">階段二：Shell 與終端機（日常操作的基礎）</h2>
<p>Shell 是所有操作的介面，終端機是 shell 的容器。這兩層配置好，後續的安裝、設定、除錯效率會高很多。</p>
<h3 id="6-安裝管理工具stow--chezmoi">6. 安裝管理工具（stow / chezmoi）</h3>
<p>把 dotfile repo 裡的配置 symlink 到正確位置。具體選型和操作見<a href="/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">模組一</a>。</p>
<h3 id="7-shell-配置zshrc--bashrc">7. Shell 配置（.zshrc / .bashrc）</h3>
<p>模組化拆分、PATH 設定、alias、prompt。做完這一步，終端機操作才順手。見<a href="/blog/linux/dotfile/02-shell-config/" data-link-title="模組二：Shell 配置" data-link-desc="shell 配置檔長成一坨不敢動時回來讀 — .zshrc/.bashrc 的結構化拆分、alias/function/PATH 的模組化設計">模組二：Shell 配置</a>。</p>
<h3 id="8-終端機--編輯器">8. 終端機 + 編輯器</h3>
<p>Terminal emulator 選型、tmux/zellij、neovim 基礎配置。見<a href="/blog/linux/dotfile/03-terminal-ecosystem/" data-link-title="模組三：終端機與編輯器" data-link-desc="終端機相關工具的配置檔散落在不同位置、不確定哪些該進 dotfile repo 時回來讀">模組三：終端機與編輯器</a>。</p>
<p>macOS 用戶到階段二完成後就有一個完整的工作環境。下一步依序讀<a href="/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">模組一</a>（管理工具選型）、<a href="/blog/linux/dotfile/02-shell-config/" data-link-title="模組二：Shell 配置" data-link-desc="shell 配置檔長成一坨不敢動時回來讀 — .zshrc/.bashrc 的結構化拆分、alias/function/PATH 的模組化設計">模組二</a>（shell 配置）、<a href="/blog/linux/dotfile/03-terminal-ecosystem/" data-link-title="模組三：終端機與編輯器" data-link-desc="終端機相關工具的配置檔散落在不同位置、不確定哪些該進 dotfile repo 時回來讀">模組三</a>（終端機），然後跳到階段四的 bootstrap script。階段三是 Linux 桌面環境的設定，macOS 用戶跳過。</p>
<h2 id="階段三桌面環境linux-限定">階段三：桌面環境（Linux 限定）</h2>
<p>macOS 用戶到階段二就有一個完整的工作環境了。以下步驟是 Linux 桌面環境的設定，macOS 用戶可以跳到階段四。</p>
<h3 id="9-視窗管理器">9. 視窗管理器</h3>
<p>平鋪式 vs 浮動式的選型，Hyprland 安裝和核心配置。見<a href="/blog/linux/dotfile/04-window-management/" data-link-title="模組四：視窗管理與平鋪式工作流" data-link-desc="同時開多個視窗時的排列策略 — 手動貼齊跟自動平鋪的差距在哪、macOS 和 Linux 各有哪些工具、多螢幕怎麼處理、什麼情境值得從浮動切換到平鋪">模組四</a>和<a href="/blog/linux/dotfile/05-hyprland-config/" data-link-title="模組五：Hyprland 配置" data-link-desc="要在 Linux 上設定 Hyprland 平鋪式桌面時回來讀">模組五</a>。</p>
<h3 id="10-桌面配套工具--rice">10. 桌面配套工具 + Rice</h3>
<p>waybar、wofi、mako、配色系統。見<a href="/blog/linux/dotfile/06-rice-design/" data-link-title="模組六：桌面 Rice 設計" data-link-desc="Hyprland 桌面從能用到好看好用 — 狀態列、啟動器、通知、鎖屏、配色系統的設計與配置">模組六：桌面 Rice 設計</a>。</p>
<h3 id="11-啟用-ssh-server--預防措施">11. 啟用 SSH server + 預防措施</h3>
<p>桌面環境可用之後，設定遠端救援通道和預防性配置：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># SSH server（出問題時可以從另一台機器救援）</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">sudo systemctl <span class="nb">enable</span> sshd
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">sudo systemctl start sshd
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"># 停用密碼登入（確保 SSH key 已設好）</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"># 編輯 /etc/ssh/sshd_config：PasswordAuthentication no</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"># swap（OOM 緩衝）</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">sudo fallocate -l 4G /swapfile
</span></span><span class="line"><span class="ln">10</span><span class="cl">sudo chmod <span class="m">600</span> /swapfile <span class="o">&amp;&amp;</span> sudo mkswap /swapfile <span class="o">&amp;&amp;</span> sudo swapon /swapfile
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># systemd-oomd</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">sudo systemctl <span class="nb">enable</span> systemd-oomd</span></span></code></pre></div><p>為什麼放在桌面設定之後：SSH server 和 swap 是預防措施，桌面能用了才有東西要保護。但 SSH key pair（階段一步驟 2-3）要提前做——key pair 是認證基礎設施，server 只是把門打開。</p>
<p>詳細的故障場景和預防措施見<a href="/blog/linux/dotfile/07-desktop-maintenance/" data-link-title="模組七：桌面環境維護與故障排除" data-link-desc="桌面凍結、compositor 掛了、或某個工具不回應時回來讀 — Linux 桌面的故障隔離模型、常見故障場景的恢復操作、日誌判讀與診斷工具">模組七：桌面環境維護與故障排除</a>。</p>
<h2 id="階段四同步與可攜性">階段四：同步與可攜性</h2>
<p>環境建好之後，確保這份配置能搬到下一台機器。</p>
<h3 id="12-bootstrap-script">12. Bootstrap script</h3>
<p>把階段一到三的操作自動化成 script，下次換機器跑一次就好。見<a href="/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">模組八：同步、Bootstrap 與環境重建</a>。</p>
<h3 id="13-secret-管理">13. Secret 管理</h3>
<p>哪些東西該進 repo、哪些要排除（SSH private key、API token、密碼）。同樣見<a href="/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">模組八</a>。</p>
<h2 id="依賴關係速查">依賴關係速查</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">OS 安裝
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  └─ SSH key pair ← 後續所有 Git / SSH 操作的前提
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">       └─ Git 安裝
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">            └─ dotfile repo clone
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">                 └─ 管理工具（stow link）
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">                      ├─ Shell 配置
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">                      ├─ 終端機 + 編輯器
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">                      └─ 桌面環境（Linux，macOS 到此為止 → 直接跳階段四）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">                           └─ SSH server + 預防措施
</span></span><span class="line"><span class="ln">10</span><span class="cl">                                └─ Bootstrap script 自動化</span></span></code></pre></div><h2 id="可以亂序的步驟">可以亂序的步驟</h2>
<p>依賴圖裡<strong>同一層級的步驟</strong>可以調換順序。具體來說：</p>
<ul>
<li>Shell 配置、終端機配置、編輯器配置三者互不依賴，先做哪個都行</li>
<li>視窗管理器和桌面配套工具可以交替設定（先裝 Hyprland 再裝 waybar，或反過來）</li>
<li>swap 和 SSH server 互不依賴，先做哪個都行</li>
</ul>
<p>跨層級的依賴必須按順序：SSH key 是 clone repo 的前提，repo 是 stow link 的前提，stow link 是 shell 配置生效的前提。</p>
]]></content:encoded></item><item><title>CI gate 與 workflow 邊界</title><link>https://tarrragon.github.io/blog/ci/ci-gate-workflow-boundary/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/ci-gate-workflow-boundary/</guid><description>&lt;p>CI gate 的核心責任是把「是否進入下一階段」變成明確條件。測試、建置、發布與人工審核可以分成不同 workflow 或 job，但只要它們共同決定同一次發布，就需要有清楚的 gate 關係。&lt;/p>
&lt;h2 id="gate-形式">Gate 形式&lt;/h2>
&lt;p>Gate 形式要依控制範圍選擇。PR 合併、job 執行順序、production 發布與 artifact 傳遞是四種不同責任，混在一起會讓紅燈的意義變模糊。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Gate 形式&lt;/th>
 &lt;th>責任&lt;/th>
 &lt;th>判讀方式&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/required-checks/" data-link-title="Required Checks" data-link-desc="說明 pull request 的必要檢查如何作為合併 gate">Required checks&lt;/a>&lt;/td>
 &lt;td>阻止未通過測試的 commit 合併&lt;/td>
 &lt;td>PR 或 branch protection 顯示必須通過&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Job &lt;code>needs&lt;/code>&lt;/td>
 &lt;td>讓 deploy 等 test / build&lt;/td>
 &lt;td>同一 workflow 內 deploy 依賴前置 job&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">Environment protection&lt;/a>&lt;/td>
 &lt;td>控制 production / target environment 發布&lt;/td>
 &lt;td>部署環境需要審核或 required reviewers&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact handoff&lt;/a>&lt;/td>
 &lt;td>確保測試與發布使用同一份產物&lt;/td>
 &lt;td>test job 產生 artifact，deploy job 使用&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/required-checks/" data-link-title="Required Checks" data-link-desc="說明 pull request 的必要檢查如何作為合併 gate">Required checks&lt;/a> 適合保護主線。它讓測試結果成為合併條件，避免紅燈變更進入 &lt;code>main&lt;/code> 或 release branch（backend 延伸見 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/ci-pipeline/" data-link-title="CI Pipeline" data-link-desc="說明持續整合流程如何在合併前驗證品質與相容性">CI Pipeline&lt;/a>）。&lt;/p>
&lt;p>Job &lt;code>needs&lt;/code> 適合同一條 workflow 內的發布管線。它讓 &lt;code>deploy&lt;/code> 必須等 &lt;code>test&lt;/code>、&lt;code>build&lt;/code> 或 &lt;code>package&lt;/code> 成功後才執行，避免 deploy job 先於驗證結果流動（platform 延伸見 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/deployment-contract/" data-link-title="Deployment Contract" data-link-desc="說明服務與部署平台之間的生命週期約定">Deployment Contract&lt;/a>）。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">Environment protection&lt;/a> 適合正式環境。即使 build 與測試通過，production 或其他目標環境仍可要求人工審核、特定分支或特定 reviewer 才能部署（治理延伸見 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">Release Gate&lt;/a>）。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact handoff&lt;/a> 適合避免「測試一份、發布另一份」的漂移。較嚴謹的流程會讓 build job 產生 artifact，test job 驗證這份 artifact，deploy job 發布同一份 artifact（供應鏈延伸見 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/artifact-provenance/" data-link-title="Artifact Provenance" data-link-desc="說明交付物的來源、完整性與簽章關聯如何建立信任">Artifact Provenance&lt;/a>）。&lt;/p>
&lt;h2 id="workflow-邊界">Workflow 邊界&lt;/h2>
&lt;p>Workflow 邊界的責任是決定哪些步驟共享同一條執行圖。放在同一條 workflow 裡的 job 可以用 &lt;code>needs&lt;/code> 建立顯式依賴；分散在不同 workflow 裡的流程，通常要靠 branch protection 或 environment protection 建立跨 workflow gate。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>結構&lt;/th>
 &lt;th>適合情境&lt;/th>
 &lt;th>常見風險&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>單一 workflow 多 job&lt;/td>
 &lt;td>test / build / deploy 緊密相依&lt;/td>
 &lt;td>YAML 變長，但依賴關係清楚&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>多 workflow&lt;/td>
 &lt;td>不同觸發條件或責任完全不同&lt;/td>
 &lt;td>跨 workflow gate 要靠 repo 設定&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>PR workflow + deploy&lt;/td>
 &lt;td>PR 驗證、main 發布分離&lt;/td>
 &lt;td>main push 若缺 required checks 會漏&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Artifact pipeline&lt;/td>
 &lt;td>同一份產物要被測試再發布&lt;/td>
 &lt;td>artifact 版本與權限要治理&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>多 workflow 的關鍵風險是順序假設。GitHub Actions 的 workflow 彼此獨立；跨 workflow 順序需要靠 repository 設定或 API 顯式串接。&lt;/p></description><content:encoded><![CDATA[<p>CI gate 的核心責任是把「是否進入下一階段」變成明確條件。測試、建置、發布與人工審核可以分成不同 workflow 或 job，但只要它們共同決定同一次發布，就需要有清楚的 gate 關係。</p>
<h2 id="gate-形式">Gate 形式</h2>
<p>Gate 形式要依控制範圍選擇。PR 合併、job 執行順序、production 發布與 artifact 傳遞是四種不同責任，混在一起會讓紅燈的意義變模糊。</p>
<table>
  <thead>
      <tr>
          <th>Gate 形式</th>
          <th>責任</th>
          <th>判讀方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/required-checks/" data-link-title="Required Checks" data-link-desc="說明 pull request 的必要檢查如何作為合併 gate">Required checks</a></td>
          <td>阻止未通過測試的 commit 合併</td>
          <td>PR 或 branch protection 顯示必須通過</td>
      </tr>
      <tr>
          <td>Job <code>needs</code></td>
          <td>讓 deploy 等 test / build</td>
          <td>同一 workflow 內 deploy 依賴前置 job</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">Environment protection</a></td>
          <td>控制 production / target environment 發布</td>
          <td>部署環境需要審核或 required reviewers</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact handoff</a></td>
          <td>確保測試與發布使用同一份產物</td>
          <td>test job 產生 artifact，deploy job 使用</td>
      </tr>
  </tbody>
</table>
<p><a href="/blog/ci/knowledge-cards/required-checks/" data-link-title="Required Checks" data-link-desc="說明 pull request 的必要檢查如何作為合併 gate">Required checks</a> 適合保護主線。它讓測試結果成為合併條件，避免紅燈變更進入 <code>main</code> 或 release branch（backend 延伸見 <a href="/blog/backend/knowledge-cards/ci-pipeline/" data-link-title="CI Pipeline" data-link-desc="說明持續整合流程如何在合併前驗證品質與相容性">CI Pipeline</a>）。</p>
<p>Job <code>needs</code> 適合同一條 workflow 內的發布管線。它讓 <code>deploy</code> 必須等 <code>test</code>、<code>build</code> 或 <code>package</code> 成功後才執行，避免 deploy job 先於驗證結果流動（platform 延伸見 <a href="/blog/backend/knowledge-cards/deployment-contract/" data-link-title="Deployment Contract" data-link-desc="說明服務與部署平台之間的生命週期約定">Deployment Contract</a>）。</p>
<p><a href="/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">Environment protection</a> 適合正式環境。即使 build 與測試通過，production 或其他目標環境仍可要求人工審核、特定分支或特定 reviewer 才能部署（治理延伸見 <a href="/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">Release Gate</a>）。</p>
<p><a href="/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact handoff</a> 適合避免「測試一份、發布另一份」的漂移。較嚴謹的流程會讓 build job 產生 artifact，test job 驗證這份 artifact，deploy job 發布同一份 artifact（供應鏈延伸見 <a href="/blog/backend/knowledge-cards/artifact-provenance/" data-link-title="Artifact Provenance" data-link-desc="說明交付物的來源、完整性與簽章關聯如何建立信任">Artifact Provenance</a>）。</p>
<h2 id="workflow-邊界">Workflow 邊界</h2>
<p>Workflow 邊界的責任是決定哪些步驟共享同一條執行圖。放在同一條 workflow 裡的 job 可以用 <code>needs</code> 建立顯式依賴；分散在不同 workflow 裡的流程，通常要靠 branch protection 或 environment protection 建立跨 workflow gate。</p>
<table>
  <thead>
      <tr>
          <th>結構</th>
          <th>適合情境</th>
          <th>常見風險</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>單一 workflow 多 job</td>
          <td>test / build / deploy 緊密相依</td>
          <td>YAML 變長，但依賴關係清楚</td>
      </tr>
      <tr>
          <td>多 workflow</td>
          <td>不同觸發條件或責任完全不同</td>
          <td>跨 workflow gate 要靠 repo 設定</td>
      </tr>
      <tr>
          <td>PR workflow + deploy</td>
          <td>PR 驗證、main 發布分離</td>
          <td>main push 若缺 required checks 會漏</td>
      </tr>
      <tr>
          <td>Artifact pipeline</td>
          <td>同一份產物要被測試再發布</td>
          <td>artifact 版本與權限要治理</td>
      </tr>
  </tbody>
</table>
<p>多 workflow 的關鍵風險是順序假設。GitHub Actions 的 workflow 彼此獨立；跨 workflow 順序需要靠 repository 設定或 API 顯式串接。</p>
<h2 id="發布阻擋判讀">發布阻擋判讀</h2>
<p>發布阻擋要同時看 YAML 與 GitHub repository 設定。YAML 說明 workflow 或 job 如何執行；跨 workflow 的「測試通過才發布」通常要靠 <a href="/blog/ci/knowledge-cards/branch-protection/" data-link-title="Branch Protection" data-link-desc="說明主線分支如何以規則保護合併與發布前置條件">Branch Protection</a>、required status checks 或 environment protection。</p>
<table>
  <thead>
      <tr>
          <th>問題</th>
          <th>只看 YAML 能判斷嗎</th>
          <th>應檢查的位置</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>deploy 是否等 build</td>
          <td>可以</td>
          <td>同 workflow 的 <code>needs</code></td>
      </tr>
      <tr>
          <td>deploy 是否等另一條 test workflow</td>
          <td>通常要查設定</td>
          <td><a href="/blog/ci/knowledge-cards/branch-protection/" data-link-title="Branch Protection" data-link-desc="說明主線分支如何以規則保護合併與發布前置條件">Branch Protection</a> / <a href="/blog/ci/knowledge-cards/required-checks/" data-link-title="Required Checks" data-link-desc="說明 pull request 的必要檢查如何作為合併 gate">Required Checks</a></td>
      </tr>
      <tr>
          <td>PR 是否必須通過測試才能合併</td>
          <td>需要查 repo 設定</td>
          <td><a href="/blog/ci/knowledge-cards/branch-protection/" data-link-title="Branch Protection" data-link-desc="說明主線分支如何以規則保護合併與發布前置條件">Branch Protection</a></td>
      </tr>
      <tr>
          <td>目標環境是否需要人工審核</td>
          <td>需要查環境設定</td>
          <td>Environment protection</td>
      </tr>
      <tr>
          <td>測試與發布是否同一份 artifact</td>
          <td>可以部分判斷</td>
          <td>workflow artifact upload / download</td>
      </tr>
  </tbody>
</table>
<p>這個判讀順序能避免錯修。若測試紅燈但目標環境仍發布，問題通常在 deploy gate 尚未把測試狀態納入發布條件。</p>
<h2 id="常見反模式">常見反模式</h2>
<p>反模式的共同問題是讓 CI 綠燈與發布安全之間失去因果關係。CI 的目標是讓綠燈代表「這次變更在定義好的條件下可進下一階段」。</p>
<table>
  <thead>
      <tr>
          <th>反模式</th>
          <th>風險</th>
          <th>替代做法</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>deploy workflow 不等 test</td>
          <td>測試紅燈仍可能發布</td>
          <td>用 required checks 或 <code>needs</code></td>
      </tr>
      <tr>
          <td>CI 與本機命令不同</td>
          <td>本機通過但 CI 失敗</td>
          <td>把命令收斂到 Makefile / npm script</td>
      </tr>
      <tr>
          <td>測試與發布各自 build</td>
          <td>測試產物與發布產物漂移</td>
          <td>用 artifact handoff</td>
      </tr>
      <tr>
          <td>看到紅燈直接重跑</td>
          <td>掩蓋 flaky 或環境問題</td>
          <td>先看失敗 log，再決定是否重跑</td>
      </tr>
      <tr>
          <td>用 <code>--no-verify</code> 或跳過 CI</td>
          <td>把局部問題帶進主線</td>
          <td>修掉 gate 或明確記錄例外</td>
      </tr>
  </tbody>
</table>
<h2 id="tripwire">Tripwire</h2>
<p>Tripwire 的責任是提示什麼時候 workflow 結構需要重切，讓團隊從局部 patch 回到 gate 設計。</p>
<ul>
<li>測試紅燈仍發布：把 deploy gate 顯式化，使用 required checks 或同 workflow <code>needs</code>。</li>
<li>本機常常重現不出 CI：把命令收斂到 <code>Makefile</code> 或 <code>npm scripts</code>，減少 workflow 專屬命令。</li>
<li>測試常因 artifact 缺失失敗：建立 artifact handoff，讓測試與發布使用同一份產物。</li>
<li>workflow 說明與實作分叉：同步更新 workflow 文件與 YAML，讓維護入口保持可信。</li>
</ul>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>CI 紅燈處理流程：讀 <a href="../github-actions-failure-flow/">CI 失敗到修復發布流程</a>。</li>
<li>靜態站部署案例：讀 <a href="../blog-project-deploy/">本 blog 專案部署</a>。</li>
<li>可靠性層的 release gate：讀 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate 與變更節奏</a>。</li>
</ul>
]]></content:encoded></item><item><title>Dotfile 工作環境配置指南</title><link>https://tarrragon.github.io/blog/linux/dotfile/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/</guid><description>&lt;p>開發者的工作環境是一組配置檔的集合：shell 怎麼設定、編輯器用什麼快捷鍵、終端機長什麼樣、視窗怎麼排列。這些配置檔在 Unix 系統上以 &lt;code>.&lt;/code> 開頭（隱藏檔），統稱 dotfile。&lt;/p>
&lt;p>dotfile 管理解決的核心問題是&lt;strong>環境可重現性&lt;/strong>。一台新機器、一次重灌、一個 VM，都應該能用一份 Git repo 還原成你熟悉的工作環境。不做管理的代價是：每次換機器都在重新手動設定，每次都少記一兩個東西，每次都花一兩天才回到順手的狀態。&lt;/p>
&lt;h2 id="和其他系列的關係">和其他系列的關係&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>系列&lt;/th>
 &lt;th>聚焦&lt;/th>
 &lt;th>和 Dotfile 的交集&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/infra/" data-link-title="Infra 基礎設施建置指南" data-link-desc="從零循序漸進把雲端基礎設施做起來 — IaC、身分憑證、網路地基、環境分離、核心服務、可觀測性、自動化 review 與治理習慣，含怎麼在組織內推動">Infra&lt;/a>&lt;/td>
 &lt;td>雲端基礎設施地基（IaC、網路、身分、環境分離）&lt;/td>
 &lt;td>Infra 是組織的環境 as code，Dotfile 是個人的環境 as code，思想平行&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/devops/" data-link-title="DevOps 實務指南" data-link-desc="負載平衡、水平擴展、流量管控、服務探活、容量規劃、高可用、突發流量、成本管理 — 服務營運的工程基礎">DevOps&lt;/a>&lt;/td>
 &lt;td>服務營運（負載、擴展、容量、成本）&lt;/td>
 &lt;td>DevOps 工程師的日常工具鏈（多終端機、SSH、log tail）正是 dotfile 高度客製的場景&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/tools/cli/" data-link-title="CLI 環境工具" data-link-desc="在純文字終端機下做事時要挑工具——日常指令的現代替代品（rg/fd/fzf），以及用文字字元做出圖形化操作介面的 TUI（監控、圖表、多工器、資料庫）——想知道各情境有哪些選擇時回來讀">CLI&lt;/a>&lt;/td>
 &lt;td>TUI 工具、多工器、檔案管理器&lt;/td>
 &lt;td>CLI 工具的配置檔是 dotfile 管理的核心對象&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/monitoring/" data-link-title="監控實務指南" data-link-desc="整理非伺服器端運行時的監控體系 — 行為蒐集、錯誤回報、效能指標、生命週期追蹤，從自架方案到商業方案的完整知識路線">Monitoring&lt;/a>&lt;/td>
 &lt;td>客戶端監控體系&lt;/td>
 &lt;td>Monitoring 有獨立的 hands-on 專案做實測，Dotfile 也會有 VM 實測專案&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Infra 教「組織的地基怎麼用 code 管理」，Dotfile 教「個人的工作桌面怎麼用 code 管理」。模組九把兩者接起來——當個人 dotfile 的思想擴展到團隊，就是 devcontainer 和標準化開發環境。&lt;/p>
&lt;h2 id="地基機器初始化">地基：機器初始化&lt;/h2>
&lt;p>編號模組都假設你已經有一台裝好 Linux、工具齊全、能從外部連入的機器。&lt;a href="https://tarrragon.github.io/blog/linux/install/" data-link-title="Linux 安裝與機器初始化" data-link-desc="在 VM 或新機器從零裝好 Linux、判讀安裝程式選項、驗證最小系統、或要從外部連入跑 bootstrap 時回來讀">Linux 安裝與機器初始化&lt;/a> 補的是那段被預設跳過的地基——OS 安裝選項怎麼判讀、最小系統缺哪些必要工具、怎麼連進去跑 bootstrap（含還沒有 SSH key 的情況），以及怎麼讓安裝腳本在失敗時可診斷。只要你換到全新環境（開 VM、租雲端主機、拿到空機器），就會先撞上這層。內容來自一次完整的 VM 實測，蒸餾成不綁特定發行版的判讀與決策。&lt;/p>
&lt;h2 id="教學模組">教學模組&lt;/h2>
&lt;p>模組編號反映學習路徑：先理解為什麼、再學怎麼管理、然後逐層處理 shell/終端機/視窗管理/視覺客製化，最後談維護排錯、同步可攜性和團隊化。模組四到七是 Linux 桌面環境的深度實作，之後會搭配 VM 專案做 hands-on 實測。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>模組&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;th>回答什麼問題&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/00-dotfile-mindset/" data-link-title="模組零：Dotfile 心智模型" data-link-desc="換機器、開 VM、重灌系統時需要快速還原開發環境，或想釐清哪些配置該版控、哪些該排除時回來讀">模組零：Dotfile 心智模型&lt;/a>&lt;/td>
 &lt;td>什麼是 dotfile、為什麼要管理、環境可重現性&lt;/td>
 &lt;td>為什麼不能每次手動設定就好&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">模組一：管理工具與目錄結構&lt;/a>&lt;/td>
 &lt;td>bare repo / stow / chezmoi、目錄設計、版控工作流&lt;/td>
 &lt;td>dotfile 怎麼用 Git 管、目錄該怎麼組織&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/02-shell-config/" data-link-title="模組二：Shell 配置" data-link-desc="shell 配置檔長成一坨不敢動時回來讀 — .zshrc/.bashrc 的結構化拆分、alias/function/PATH 的模組化設計">模組二：Shell 配置&lt;/a>&lt;/td>
 &lt;td>zsh/bash 結構化配置、模組化拆分、alias/function/PATH&lt;/td>
 &lt;td>.zshrc 該怎麼寫才不會長成一坨&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/03-terminal-ecosystem/" data-link-title="模組三：終端機與編輯器" data-link-desc="終端機相關工具的配置檔散落在不同位置、不確定哪些該進 dotfile repo 時回來讀">模組三：終端機與編輯器&lt;/a>&lt;/td>
 &lt;td>terminal emulator 選型、tmux/zellij、neovim 基礎&lt;/td>
 &lt;td>終端機生態的配置檔有哪些、怎麼管理&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/04-window-management/" data-link-title="模組四：視窗管理與平鋪式工作流" data-link-desc="同時開多個視窗時的排列策略 — 手動貼齊跟自動平鋪的差距在哪、macOS 和 Linux 各有哪些工具、多螢幕怎麼處理、什麼情境值得從浮動切換到平鋪">模組四：視窗管理與平鋪式工作流&lt;/a>&lt;/td>
 &lt;td>手動 vs 自動平鋪、macOS 工具鏈、Linux tiling WM&lt;/td>
 &lt;td>Rectangle 不夠用的時候該換什麼&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/" data-link-title="模組五：Hyprland 配置" data-link-desc="要在 Linux 上設定 Hyprland 平鋪式桌面時回來讀">模組五：Hyprland 配置&lt;/a>&lt;/td>
 &lt;td>Hyprland 安裝、核心設定、keybind、monitor、workspace&lt;/td>
 &lt;td>Hyprland 的配置檔怎麼寫、核心概念是什麼&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/" data-link-title="模組六：桌面 Rice 設計" data-link-desc="Hyprland 桌面從能用到好看好用 — 狀態列、啟動器、通知、鎖屏、配色系統的設計與配置">模組六：桌面 Rice 設計&lt;/a>&lt;/td>
 &lt;td>狀態列、啟動器、通知、配色系統、desktop shell&lt;/td>
 &lt;td>桌面怎麼從「能用」變成「好看又好用」&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/07-desktop-maintenance/" data-link-title="模組七：桌面環境維護與故障排除" data-link-desc="桌面凍結、compositor 掛了、或某個工具不回應時回來讀 — Linux 桌面的故障隔離模型、常見故障場景的恢復操作、日誌判讀與診斷工具">模組七：桌面環境維護與故障排除&lt;/a>&lt;/td>
 &lt;td>故障隔離模型、常見故障恢復、日誌判讀&lt;/td>
 &lt;td>compositor 掛了或桌面凍結時怎麼診斷和恢復&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">模組八：同步、Bootstrap 與環境重建&lt;/a>&lt;/td>
 &lt;td>跨機器同步、install script、secret 排除、VM 對比&lt;/td>
 &lt;td>換機器時怎麼一鍵還原、哪些東西不該進 repo&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/09-team-environment/" data-link-title="模組九：從個人到團隊" data-link-desc="個人 dotfile 管理的思想要延伸到團隊開發環境標準化時回來讀 — devcontainer、nix、商業環境配置管理">模組九：從個人到團隊&lt;/a>&lt;/td>
 &lt;td>devcontainer、nix、商業開發環境標準化&lt;/td>
 &lt;td>個人 dotfile 的思想怎麼延伸到團隊環境管理&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="vm-實測專案">VM 實測專案&lt;/h2>
&lt;p>模組四到七的 Linux 桌面配置會搭配一個獨立的 VM 專案做 hands-on 實測（類似 &lt;a href="https://tarrragon.github.io/blog/monitoring/" data-link-title="監控實務指南" data-link-desc="整理非伺服器端運行時的監控體系 — 行為蒐集、錯誤回報、效能指標、生命週期追蹤，從自架方案到商業方案的完整知識路線">Monitoring&lt;/a> 系列搭配 monitor 專案的關係）。教材負責概念與配置邏輯的說明，VM 專案負責實際安裝、調教、截圖驗證。VM 專案會在教材完成後另外建立。&lt;/p></description><content:encoded><![CDATA[<p>開發者的工作環境是一組配置檔的集合：shell 怎麼設定、編輯器用什麼快捷鍵、終端機長什麼樣、視窗怎麼排列。這些配置檔在 Unix 系統上以 <code>.</code> 開頭（隱藏檔），統稱 dotfile。</p>
<p>dotfile 管理解決的核心問題是<strong>環境可重現性</strong>。一台新機器、一次重灌、一個 VM，都應該能用一份 Git repo 還原成你熟悉的工作環境。不做管理的代價是：每次換機器都在重新手動設定，每次都少記一兩個東西，每次都花一兩天才回到順手的狀態。</p>
<h2 id="和其他系列的關係">和其他系列的關係</h2>
<table>
  <thead>
      <tr>
          <th>系列</th>
          <th>聚焦</th>
          <th>和 Dotfile 的交集</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/infra/" data-link-title="Infra 基礎設施建置指南" data-link-desc="從零循序漸進把雲端基礎設施做起來 — IaC、身分憑證、網路地基、環境分離、核心服務、可觀測性、自動化 review 與治理習慣，含怎麼在組織內推動">Infra</a></td>
          <td>雲端基礎設施地基（IaC、網路、身分、環境分離）</td>
          <td>Infra 是組織的環境 as code，Dotfile 是個人的環境 as code，思想平行</td>
      </tr>
      <tr>
          <td><a href="/blog/devops/" data-link-title="DevOps 實務指南" data-link-desc="負載平衡、水平擴展、流量管控、服務探活、容量規劃、高可用、突發流量、成本管理 — 服務營運的工程基礎">DevOps</a></td>
          <td>服務營運（負載、擴展、容量、成本）</td>
          <td>DevOps 工程師的日常工具鏈（多終端機、SSH、log tail）正是 dotfile 高度客製的場景</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/tools/cli/" data-link-title="CLI 環境工具" data-link-desc="在純文字終端機下做事時要挑工具——日常指令的現代替代品（rg/fd/fzf），以及用文字字元做出圖形化操作介面的 TUI（監控、圖表、多工器、資料庫）——想知道各情境有哪些選擇時回來讀">CLI</a></td>
          <td>TUI 工具、多工器、檔案管理器</td>
          <td>CLI 工具的配置檔是 dotfile 管理的核心對象</td>
      </tr>
      <tr>
          <td><a href="/blog/monitoring/" data-link-title="監控實務指南" data-link-desc="整理非伺服器端運行時的監控體系 — 行為蒐集、錯誤回報、效能指標、生命週期追蹤，從自架方案到商業方案的完整知識路線">Monitoring</a></td>
          <td>客戶端監控體系</td>
          <td>Monitoring 有獨立的 hands-on 專案做實測，Dotfile 也會有 VM 實測專案</td>
      </tr>
  </tbody>
</table>
<p>Infra 教「組織的地基怎麼用 code 管理」，Dotfile 教「個人的工作桌面怎麼用 code 管理」。模組九把兩者接起來——當個人 dotfile 的思想擴展到團隊，就是 devcontainer 和標準化開發環境。</p>
<h2 id="地基機器初始化">地基：機器初始化</h2>
<p>編號模組都假設你已經有一台裝好 Linux、工具齊全、能從外部連入的機器。<a href="/blog/linux/install/" data-link-title="Linux 安裝與機器初始化" data-link-desc="在 VM 或新機器從零裝好 Linux、判讀安裝程式選項、驗證最小系統、或要從外部連入跑 bootstrap 時回來讀">Linux 安裝與機器初始化</a> 補的是那段被預設跳過的地基——OS 安裝選項怎麼判讀、最小系統缺哪些必要工具、怎麼連進去跑 bootstrap（含還沒有 SSH key 的情況），以及怎麼讓安裝腳本在失敗時可診斷。只要你換到全新環境（開 VM、租雲端主機、拿到空機器），就會先撞上這層。內容來自一次完整的 VM 實測，蒸餾成不綁特定發行版的判讀與決策。</p>
<h2 id="教學模組">教學模組</h2>
<p>模組編號反映學習路徑：先理解為什麼、再學怎麼管理、然後逐層處理 shell/終端機/視窗管理/視覺客製化，最後談維護排錯、同步可攜性和團隊化。模組四到七是 Linux 桌面環境的深度實作，之後會搭配 VM 專案做 hands-on 實測。</p>
<table>
  <thead>
      <tr>
          <th>模組</th>
          <th>主題</th>
          <th>回答什麼問題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/linux/dotfile/00-dotfile-mindset/" data-link-title="模組零：Dotfile 心智模型" data-link-desc="換機器、開 VM、重灌系統時需要快速還原開發環境，或想釐清哪些配置該版控、哪些該排除時回來讀">模組零：Dotfile 心智模型</a></td>
          <td>什麼是 dotfile、為什麼要管理、環境可重現性</td>
          <td>為什麼不能每次手動設定就好</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">模組一：管理工具與目錄結構</a></td>
          <td>bare repo / stow / chezmoi、目錄設計、版控工作流</td>
          <td>dotfile 怎麼用 Git 管、目錄該怎麼組織</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/02-shell-config/" data-link-title="模組二：Shell 配置" data-link-desc="shell 配置檔長成一坨不敢動時回來讀 — .zshrc/.bashrc 的結構化拆分、alias/function/PATH 的模組化設計">模組二：Shell 配置</a></td>
          <td>zsh/bash 結構化配置、模組化拆分、alias/function/PATH</td>
          <td>.zshrc 該怎麼寫才不會長成一坨</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/03-terminal-ecosystem/" data-link-title="模組三：終端機與編輯器" data-link-desc="終端機相關工具的配置檔散落在不同位置、不確定哪些該進 dotfile repo 時回來讀">模組三：終端機與編輯器</a></td>
          <td>terminal emulator 選型、tmux/zellij、neovim 基礎</td>
          <td>終端機生態的配置檔有哪些、怎麼管理</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/04-window-management/" data-link-title="模組四：視窗管理與平鋪式工作流" data-link-desc="同時開多個視窗時的排列策略 — 手動貼齊跟自動平鋪的差距在哪、macOS 和 Linux 各有哪些工具、多螢幕怎麼處理、什麼情境值得從浮動切換到平鋪">模組四：視窗管理與平鋪式工作流</a></td>
          <td>手動 vs 自動平鋪、macOS 工具鏈、Linux tiling WM</td>
          <td>Rectangle 不夠用的時候該換什麼</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/05-hyprland-config/" data-link-title="模組五：Hyprland 配置" data-link-desc="要在 Linux 上設定 Hyprland 平鋪式桌面時回來讀">模組五：Hyprland 配置</a></td>
          <td>Hyprland 安裝、核心設定、keybind、monitor、workspace</td>
          <td>Hyprland 的配置檔怎麼寫、核心概念是什麼</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/06-rice-design/" data-link-title="模組六：桌面 Rice 設計" data-link-desc="Hyprland 桌面從能用到好看好用 — 狀態列、啟動器、通知、鎖屏、配色系統的設計與配置">模組六：桌面 Rice 設計</a></td>
          <td>狀態列、啟動器、通知、配色系統、desktop shell</td>
          <td>桌面怎麼從「能用」變成「好看又好用」</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/07-desktop-maintenance/" data-link-title="模組七：桌面環境維護與故障排除" data-link-desc="桌面凍結、compositor 掛了、或某個工具不回應時回來讀 — Linux 桌面的故障隔離模型、常見故障場景的恢復操作、日誌判讀與診斷工具">模組七：桌面環境維護與故障排除</a></td>
          <td>故障隔離模型、常見故障恢復、日誌判讀</td>
          <td>compositor 掛了或桌面凍結時怎麼診斷和恢復</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">模組八：同步、Bootstrap 與環境重建</a></td>
          <td>跨機器同步、install script、secret 排除、VM 對比</td>
          <td>換機器時怎麼一鍵還原、哪些東西不該進 repo</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/09-team-environment/" data-link-title="模組九：從個人到團隊" data-link-desc="個人 dotfile 管理的思想要延伸到團隊開發環境標準化時回來讀 — devcontainer、nix、商業環境配置管理">模組九：從個人到團隊</a></td>
          <td>devcontainer、nix、商業開發環境標準化</td>
          <td>個人 dotfile 的思想怎麼延伸到團隊環境管理</td>
      </tr>
  </tbody>
</table>
<h2 id="vm-實測專案">VM 實測專案</h2>
<p>模組四到七的 Linux 桌面配置會搭配一個獨立的 VM 專案做 hands-on 實測（類似 <a href="/blog/monitoring/" data-link-title="監控實務指南" data-link-desc="整理非伺服器端運行時的監控體系 — 行為蒐集、錯誤回報、效能指標、生命週期追蹤，從自架方案到商業方案的完整知識路線">Monitoring</a> 系列搭配 monitor 專案的關係）。教材負責概念與配置邏輯的說明，VM 專案負責實際安裝、調教、截圖驗證。VM 專案會在教材完成後另外建立。</p>
]]></content:encoded></item><item><title>模組四：視窗管理與平鋪式工作流</title><link>https://tarrragon.github.io/blog/linux/dotfile/04-window-management/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/04-window-management/</guid><description>&lt;p>視窗管理器（window manager, WM）負責決定螢幕上的視窗怎麼排列、怎麼切換、怎麼調整大小。每個桌面環境都有一個 WM，差別在於它是讓你用滑鼠自己拖，還是按規則自動幫你排好。&lt;/p>
&lt;h2 id="章節文章">章節文章&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>文章&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/04-window-management/floating-vs-tiling/" data-link-title="浮動式 vs 平鋪式視窗管理" data-link-desc="在手動貼齊（Rectangle）跟自動平鋪之間猶豫、或想評估自己的工作型態適不適合平鋪式 WM 時回來讀">浮動式 vs 平鋪式&lt;/a>&lt;/td>
 &lt;td>兩種視窗管理模式的差異、手動貼齊 vs 自動平鋪的三層差距、適用判讀&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/04-window-management/macos-window-tools/" data-link-title="macOS 視窗管理工具鏈" data-link-desc="macOS 上想用鍵盤管理視窗、不確定該用哪個工具時回來讀">macOS 視窗管理工具鏈&lt;/a>&lt;/td>
 &lt;td>Rectangle / Amethyst / AeroSpace / yabai 選型判讀與配置範例&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/04-window-management/linux-tiling-wm/" data-link-title="Linux Tiling WM 生態" data-link-desc="要在 Linux 上選 tiling WM（i3/sway/Hyprland/bspwm）或理解 Wayland vs X11 差異時回來讀">Linux Tiling WM 生態&lt;/a>&lt;/td>
 &lt;td>主流 tiling WM 比較（i3/sway/Hyprland/bspwm/dwm）、多螢幕處理、dotfile 中的角色&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/04-window-management/wayland-explainer/" data-link-title="Wayland 顯示協議：為什麼 Hyprland 不跑在 X11 上" data-link-desc="想理解 Hyprland 底層的圖形架構、Wayland 跟 X11 的差異、XWayland 相容層、以及 2026 年 Wayland 已經是主流這件事時回來讀">Wayland 顯示協議&lt;/a>&lt;/td>
 &lt;td>Wayland 架構、跟 X11 的差異、XWayland 相容層、2026 採用現況、為什麼 tiling WM 選 Wayland&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>macOS 讀者的主線是前兩篇（浮動 vs 平鋪、macOS 工具鏈）。Linux Tiling WM 生態和 Wayland 顯示協議是想在 VM 或實機上體驗 Linux 桌面的選讀——macOS 上用 AeroSpace 或 yabai 的讀者可以直接跳到&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">同步與 Bootstrap&lt;/a>。&lt;/p>
&lt;h2 id="跨分類引用">跨分類引用&lt;/h2>
&lt;ul>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/" data-link-title="模組五：Hyprland 配置" data-link-desc="要在 Linux 上設定 Hyprland 平鋪式桌面時回來讀">模組五：Hyprland 配置&lt;/a>：Hyprland 的完整配置實務&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/" data-link-title="模組六：桌面 Rice 設計" data-link-desc="Hyprland 桌面從能用到好看好用 — 狀態列、啟動器、通知、鎖屏、配色系統的設計與配置">模組六：桌面 Rice 設計&lt;/a>：compositor 之上的狀態列、啟動器、通知、配色系統&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">模組一：管理工具與目錄結構&lt;/a>：WM 配置怎麼進 dotfile repo&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">模組八：同步、Bootstrap 與環境重建&lt;/a>：跨機器搬移時硬體相關設定怎麼處理&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>視窗管理器（window manager, WM）負責決定螢幕上的視窗怎麼排列、怎麼切換、怎麼調整大小。每個桌面環境都有一個 WM，差別在於它是讓你用滑鼠自己拖，還是按規則自動幫你排好。</p>
<h2 id="章節文章">章節文章</h2>
<table>
  <thead>
      <tr>
          <th>文章</th>
          <th>主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/linux/dotfile/04-window-management/floating-vs-tiling/" data-link-title="浮動式 vs 平鋪式視窗管理" data-link-desc="在手動貼齊（Rectangle）跟自動平鋪之間猶豫、或想評估自己的工作型態適不適合平鋪式 WM 時回來讀">浮動式 vs 平鋪式</a></td>
          <td>兩種視窗管理模式的差異、手動貼齊 vs 自動平鋪的三層差距、適用判讀</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/04-window-management/macos-window-tools/" data-link-title="macOS 視窗管理工具鏈" data-link-desc="macOS 上想用鍵盤管理視窗、不確定該用哪個工具時回來讀">macOS 視窗管理工具鏈</a></td>
          <td>Rectangle / Amethyst / AeroSpace / yabai 選型判讀與配置範例</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/04-window-management/linux-tiling-wm/" data-link-title="Linux Tiling WM 生態" data-link-desc="要在 Linux 上選 tiling WM（i3/sway/Hyprland/bspwm）或理解 Wayland vs X11 差異時回來讀">Linux Tiling WM 生態</a></td>
          <td>主流 tiling WM 比較（i3/sway/Hyprland/bspwm/dwm）、多螢幕處理、dotfile 中的角色</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/04-window-management/wayland-explainer/" data-link-title="Wayland 顯示協議：為什麼 Hyprland 不跑在 X11 上" data-link-desc="想理解 Hyprland 底層的圖形架構、Wayland 跟 X11 的差異、XWayland 相容層、以及 2026 年 Wayland 已經是主流這件事時回來讀">Wayland 顯示協議</a></td>
          <td>Wayland 架構、跟 X11 的差異、XWayland 相容層、2026 採用現況、為什麼 tiling WM 選 Wayland</td>
      </tr>
  </tbody>
</table>
<p>macOS 讀者的主線是前兩篇（浮動 vs 平鋪、macOS 工具鏈）。Linux Tiling WM 生態和 Wayland 顯示協議是想在 VM 或實機上體驗 Linux 桌面的選讀——macOS 上用 AeroSpace 或 yabai 的讀者可以直接跳到<a href="/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">同步與 Bootstrap</a>。</p>
<h2 id="跨分類引用">跨分類引用</h2>
<ul>
<li>→ <a href="/blog/linux/dotfile/05-hyprland-config/" data-link-title="模組五：Hyprland 配置" data-link-desc="要在 Linux 上設定 Hyprland 平鋪式桌面時回來讀">模組五：Hyprland 配置</a>：Hyprland 的完整配置實務</li>
<li>→ <a href="/blog/linux/dotfile/06-rice-design/" data-link-title="模組六：桌面 Rice 設計" data-link-desc="Hyprland 桌面從能用到好看好用 — 狀態列、啟動器、通知、鎖屏、配色系統的設計與配置">模組六：桌面 Rice 設計</a>：compositor 之上的狀態列、啟動器、通知、配色系統</li>
<li>→ <a href="/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">模組一：管理工具與目錄結構</a>：WM 配置怎麼進 dotfile repo</li>
<li>→ <a href="/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">模組八：同步、Bootstrap 與環境重建</a>：跨機器搬移時硬體相關設定怎麼處理</li>
</ul>
]]></content:encoded></item><item><title>4.7 Workflow 編排模式</title><link>https://tarrragon.github.io/blog/llm/04-applications/workflow-patterns/</link><pubDate>Mon, 11 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/llm/04-applications/workflow-patterns/</guid><description>&lt;p>LLM 應用很少是單一 call、多半是多次 LLM call 的組合。Multi-call 組合的模式雖然各 framework（LangGraph、LlamaIndex Workflow、各家 DAG runner）包裝不同、本質上可歸納成幾種基本模式：pipeline、router、parallel、&lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/reflection/" data-link-title="Reflection / Self-critique" data-link-desc="要求模型先輸出一版、再 critique 自己、再修改的 prompting / workflow 模式、有自身失敗模式">reflection&lt;/a>。理解這幾個模式、看到任何 LLM application 都能拆解成基本元件、判斷複雜度合不合理、識別常見反模式。&lt;/p>
&lt;p>本章寫的是這四種模式的本質、它們的失敗模式、彼此組合的方式、何時退化成 single call 更好。具體 framework 的 DAG syntax / workflow API 不在本章——這些跨 framework 差異大、半年一變、原理層級更穩。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>讀完本章後你能：&lt;/p>
&lt;ol>
&lt;li>區分四種基本 workflow 模式。&lt;/li>
&lt;li>看到一個 LLM application 時、能畫出它的 workflow 結構圖。&lt;/li>
&lt;li>判斷一個 workflow 是否該退化成 single call。&lt;/li>
&lt;li>識別 workflow 設計常見的反模式。&lt;/li>
&lt;/ol>
&lt;h2 id="llm-應用的本質是多-call-組合">LLM 應用的本質是多 Call 組合&lt;/h2>
&lt;p>單一 LLM call 解的問題有上限：&lt;/p>
&lt;ul>
&lt;li>Context 限制：再大的 &lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/context-window/" data-link-title="Context Window" data-link-desc="模型一次能處理的最大 token 數量：prompt 加生成的總和上限">context window&lt;/a> 也有上限、長文件得切。&lt;/li>
&lt;li>推理深度：複雜推理拆步驟通常比一次推完更穩。&lt;/li>
&lt;li>Tool 範圍：multi-step tool use 需要多次 call 串起來。&lt;/li>
&lt;li>多面向評估：同時要管邏輯、風格、合規時、單次 call 容易偏其中一面。&lt;/li>
&lt;/ul>
&lt;p>Multi-call 組合擴展能力範圍、代價是每多一個 call 多一份成本：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Latency&lt;/strong>：N 個 call sequential 跑是 N 倍 latency；parallel 跑也至少要 max(call latency)。&lt;/li>
&lt;li>&lt;strong>Cost&lt;/strong>：每個 call 的 token 成本累加、N 個 call 是 N 倍 cost。&lt;/li>
&lt;li>&lt;strong>失敗點&lt;/strong>：每個 call 都可能失敗、N 個 call 串起來成功率是個別成功率連乘。&lt;/li>
&lt;li>&lt;strong>複雜度&lt;/strong>：error handling、retry、partial success 處理複雜度爆炸。&lt;/li>
&lt;/ul>
&lt;p>「設計 workflow」的核心問題不是「能不能拆成多 call」、是「拆成多 call 的收益值不值得這份成本」。Workflow 設計常見的失敗是過早優化（software engineering idiom：「premature optimization is the root of all evil」、在沒有 profiling 證據前就拆結構、複雜度爆炸但無實質改進）、把簡單問題切成複雜 &lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/agent/" data-link-title="LLM Agent" data-link-desc="把控制流交給 LLM 的應用模式：自主決策、跨多步呼叫工具、人類角色從主導變監督">DAG（directed acyclic graph、有向無環圖、描述步驟依賴關係的資料結構）&lt;/a>、最終比 single call 慢、貴、難維護、品質卻沒提升。Single call 在「context 塞得下 + 任務不需要外部 tool + 失敗代價低」的情境下仍是最高 ROI 選項。&lt;/p>
&lt;p>四種基本模式各自解不同的「為什麼需要多 call」、下面逐個展開。&lt;/p>
&lt;h2 id="pipeline線性串接">Pipeline：線性串接&lt;/h2>
&lt;p>&lt;strong>結構&lt;/strong>：&lt;code>call_1 → call_2 → call_3 → ...&lt;/code>、後一個 call 用前一個的 output 當 input。&lt;/p>
&lt;p>&lt;strong>適合場景&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>任務有清楚的線性子步驟（萃取 → 摘要 → 翻譯、或 plan → execute → review）。&lt;/li>
&lt;li>每個子步驟用同個模型最划算（一個 call 撐不下、拆成幾個 call 接力）。&lt;/li>
&lt;li>子步驟輸出需要中間驗證 / 處理（前一步先過 schema 解析、再餵下一步）。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>典型例子&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>Code review pipeline：先 LLM 找問題列表 → 再 LLM 對每個問題寫修改建議 → 最後 LLM 合成 summary。&lt;/li>
&lt;li>文件處理：原文 → 萃取結構化資訊 → 套用 template → 輸出最終格式。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>失敗模式&lt;/strong>：&lt;/p></description><content:encoded><![CDATA[<p>LLM 應用很少是單一 call、多半是多次 LLM call 的組合。Multi-call 組合的模式雖然各 framework（LangGraph、LlamaIndex Workflow、各家 DAG runner）包裝不同、本質上可歸納成幾種基本模式：pipeline、router、parallel、<a href="/blog/llm/knowledge-cards/reflection/" data-link-title="Reflection / Self-critique" data-link-desc="要求模型先輸出一版、再 critique 自己、再修改的 prompting / workflow 模式、有自身失敗模式">reflection</a>。理解這幾個模式、看到任何 LLM application 都能拆解成基本元件、判斷複雜度合不合理、識別常見反模式。</p>
<p>本章寫的是這四種模式的本質、它們的失敗模式、彼此組合的方式、何時退化成 single call 更好。具體 framework 的 DAG syntax / workflow API 不在本章——這些跨 framework 差異大、半年一變、原理層級更穩。</p>
<h2 id="本章目標">本章目標</h2>
<p>讀完本章後你能：</p>
<ol>
<li>區分四種基本 workflow 模式。</li>
<li>看到一個 LLM application 時、能畫出它的 workflow 結構圖。</li>
<li>判斷一個 workflow 是否該退化成 single call。</li>
<li>識別 workflow 設計常見的反模式。</li>
</ol>
<h2 id="llm-應用的本質是多-call-組合">LLM 應用的本質是多 Call 組合</h2>
<p>單一 LLM call 解的問題有上限：</p>
<ul>
<li>Context 限制：再大的 <a href="/blog/llm/knowledge-cards/context-window/" data-link-title="Context Window" data-link-desc="模型一次能處理的最大 token 數量：prompt 加生成的總和上限">context window</a> 也有上限、長文件得切。</li>
<li>推理深度：複雜推理拆步驟通常比一次推完更穩。</li>
<li>Tool 範圍：multi-step tool use 需要多次 call 串起來。</li>
<li>多面向評估：同時要管邏輯、風格、合規時、單次 call 容易偏其中一面。</li>
</ul>
<p>Multi-call 組合擴展能力範圍、代價是每多一個 call 多一份成本：</p>
<ul>
<li><strong>Latency</strong>：N 個 call sequential 跑是 N 倍 latency；parallel 跑也至少要 max(call latency)。</li>
<li><strong>Cost</strong>：每個 call 的 token 成本累加、N 個 call 是 N 倍 cost。</li>
<li><strong>失敗點</strong>：每個 call 都可能失敗、N 個 call 串起來成功率是個別成功率連乘。</li>
<li><strong>複雜度</strong>：error handling、retry、partial success 處理複雜度爆炸。</li>
</ul>
<p>「設計 workflow」的核心問題不是「能不能拆成多 call」、是「拆成多 call 的收益值不值得這份成本」。Workflow 設計常見的失敗是過早優化（software engineering idiom：「premature optimization is the root of all evil」、在沒有 profiling 證據前就拆結構、複雜度爆炸但無實質改進）、把簡單問題切成複雜 <a href="/blog/llm/knowledge-cards/agent/" data-link-title="LLM Agent" data-link-desc="把控制流交給 LLM 的應用模式：自主決策、跨多步呼叫工具、人類角色從主導變監督">DAG（directed acyclic graph、有向無環圖、描述步驟依賴關係的資料結構）</a>、最終比 single call 慢、貴、難維護、品質卻沒提升。Single call 在「context 塞得下 + 任務不需要外部 tool + 失敗代價低」的情境下仍是最高 ROI 選項。</p>
<p>四種基本模式各自解不同的「為什麼需要多 call」、下面逐個展開。</p>
<h2 id="pipeline線性串接">Pipeline：線性串接</h2>
<p><strong>結構</strong>：<code>call_1 → call_2 → call_3 → ...</code>、後一個 call 用前一個的 output 當 input。</p>
<p><strong>適合場景</strong>：</p>
<ul>
<li>任務有清楚的線性子步驟（萃取 → 摘要 → 翻譯、或 plan → execute → review）。</li>
<li>每個子步驟用同個模型最划算（一個 call 撐不下、拆成幾個 call 接力）。</li>
<li>子步驟輸出需要中間驗證 / 處理（前一步先過 schema 解析、再餵下一步）。</li>
</ul>
<p><strong>典型例子</strong>：</p>
<ul>
<li>Code review pipeline：先 LLM 找問題列表 → 再 LLM 對每個問題寫修改建議 → 最後 LLM 合成 summary。</li>
<li>文件處理：原文 → 萃取結構化資訊 → 套用 template → 輸出最終格式。</li>
</ul>
<p><strong>失敗模式</strong>：</p>
<ul>
<li><strong>中間步驟誤差累積</strong>：第一步小錯、第二步基於錯誤輸出、第三步累積到完全跑偏。整體錯誤率是個別錯誤率連乘的補集（任一步錯整個 pipeline 錯）。</li>
<li><strong>無法檢測前段錯誤</strong>：後段沒辦法回頭修正前段、即使發現結果不對。</li>
<li><strong>過度拆解</strong>：本來 single call 能處理的事拆成 3 步、latency 跟 cost 都暴增。</li>
</ul>
<p><strong>緩解策略</strong>：</p>
<ul>
<li>中間步驟加 validation（schema 解析、簡單 sanity check）、catch 早期錯誤。</li>
<li>關鍵 pipeline 加 logging、出問題時能定位是哪一步壞。</li>
<li>定期重新評估「這個 pipeline 真的需要拆嗎」、不需要就合併回 single call。</li>
</ul>
<h2 id="router依輸入分流">Router：依輸入分流</h2>
<p><strong>結構</strong>：<code>input → classifier → path A / B / C → output</code>、依分類結果走不同處理路徑。</p>
<p><strong>適合場景</strong>：</p>
<ul>
<li>輸入類型多樣、不同類型需要不同處理（簡單 query 用小模型、複雜 query 用大模型）。</li>
<li>Cost 優化（多數簡單請求走便宜 path、少數複雜請求走貴 path）。</li>
<li>功能分流（query 是 search、summarization、還是 code edit）。</li>
</ul>
<p><strong>典型例子</strong>：</p>
<ul>
<li>客服分流：先判斷使用者意圖（查訂單 / 退貨 / 一般諮詢）、再分到對應 specialized agent。</li>
<li>模型分流：先 1B classifier 判斷複雜度、簡單問題給本地 14B、複雜問題給雲端旗艦。</li>
</ul>
<p><strong>失敗模式</strong>：</p>
<ul>
<li><strong>Classifier 錯判</strong>：分流錯了、整個 query 跑進最差 path、結果完全不對。</li>
<li><strong>Classifier 比下游還複雜</strong>：本來 router 是 cost saver、結果 classifier 本身就要強模型、變成多花錢的繞路。</li>
<li><strong>Path 設計不完整</strong>：有些 query 不符合任何 path、router 強塞到某個 path、結果差。</li>
</ul>
<p>Classifier 設計常用 <a href="/blog/llm/04-applications/application-protocols/" data-link-title="4.6 應用層協議：function calling / structured output / MCP" data-link-desc="三個常被混為一談的概念：模型能力、sampling 約束、server 協議，三者的層級差異與組合方式">structured output</a> 強制輸出 enum、避免 free-form 解析；複雜應用可能把 classifier 本身做成 <a href="/blog/llm/04-applications/tool-use-principles/" data-link-title="4.3 Tool use 原理：LLM 跟外部世界互動" data-link-desc="Structured output 是 LLM 跨入工程系統的橋、function calling 取捨、為什麼本地小模型 tool use 表現崩潰">tool use</a> 的特例。</p>
<p><strong>緩解策略</strong>：</p>
<ul>
<li>Classifier 用較弱模型但加 confidence 判讀、低 confidence 走 fallback path。</li>
<li>簡化 path 數量（3-5 個內、保留必要切分即可）。</li>
<li>設計「unknown / catch-all」path、不假設所有 input 都能歸類。</li>
</ul>
<h2 id="parallel多-call-並行結果合併">Parallel：多 Call 並行、結果合併</h2>
<p><strong>結構</strong>：<code>input → [call A, call B, call C 並行跑] → 合併 → output</code>、多次 LLM call 同時跑、結果合起來。</p>
<p><strong>適合場景</strong>：</p>
<ul>
<li>任務有獨立面向（評估一份 PR 的程式碼品質 / 安全性 / 可讀性、各自一個 call）。</li>
<li>Ensemble（同個任務跑多次、用 majority vote 提高可靠度）。</li>
<li>Multi-source retrieval（從不同來源平行拉資料、合起來餵下游）。</li>
</ul>
<p><strong>典型例子</strong>：</p>
<ul>
<li>多面向審查：同份 code 同時跑「邏輯」「風格」「安全」三個 review、合併成總評。</li>
<li>Ensemble decoding：同個 prompt 用不同 seed / temperature 跑 5 次、majority vote 拿最穩答案。</li>
</ul>
<p><strong>失敗模式</strong>：</p>
<ul>
<li><strong>合併邏輯難設計</strong>：parallel 容易、合併難。三個 reviewer 意見不一致時怎麼裁判？多數決還是加權？</li>
<li><strong>Cost 是 sequential 數倍</strong>：parallel 跑 N call 的 cost 是 sequential single 的 N 倍、latency 才有節省。</li>
<li><strong>不需要並行</strong>：本來 sequential single call 能解的事、parallel 變浪費。</li>
<li><strong>不獨立的「平行」</strong>：兩個 call 其實依賴彼此、強制 parallel 反而漏訊號。</li>
</ul>
<p><strong>緩解策略</strong>：</p>
<ul>
<li>Parallel 前先問：這些 call 真的獨立嗎？依賴的應該 sequential。</li>
<li>合併邏輯先設計、再開始 parallel；想清楚怎麼合再進 parallel。</li>
<li>Cost 監控：parallel 是 cost amplifier、生產環境注意。</li>
</ul>
<h2 id="reflection產出--評估--修正">Reflection：產出 → 評估 → 修正</h2>
<p><strong>結構</strong>：<code>產出 → critic 評估 → 依評估修正 → 再評估 → ...</code>、self-improvement loop。</p>
<p><strong>適合場景</strong>：</p>
<ul>
<li>任務有客觀評估訊號（test 跑通、規範符合、structured output 合法）。</li>
<li>一次產出品質不夠、可以迭代改善。</li>
<li>創意任務的「初稿 → 修稿」流程。</li>
</ul>
<p><strong>典型例子</strong>：</p>
<ul>
<li>Code 生成 + test 驅動：產出 code → 跑 test → 失敗的話讀 error 修正 → 再跑 test → 直到通過。</li>
<li>文章寫作：生成草稿 → critic 模型評論 → 依評論修改 → 再評論 → 直到滿意。</li>
</ul>
<p><strong>失敗模式</strong>：</p>
<ul>
<li><strong>Critic 跟 generator 共用 blind spot</strong>：兩個都同個模型、有同樣的盲點、reflection 收斂到同樣錯誤位置（如兩個都不認識某個 framework 的 API、再 reflect 也不對）。</li>
<li><strong>無限循環</strong>：沒有客觀停止訊號、reflection 一直跑、cost 爆掉。</li>
<li><strong>過度修正</strong>：每次 reflection 都改一點、累積結果變糟（過度 fitting critic 意見）。</li>
<li><strong>Critic 失職</strong>：critic 太寬鬆、什麼都說 OK；或太嚴格、什麼都挑、永遠停不下來。</li>
</ul>
<p><strong>Systematic vs random error 的關鍵分層</strong>：reflection 對 random error 有效（同 prompt 重跑因 sampling 抖動產生的錯誤、多輪重 sample 會收斂）、但對 systematic error 無效（generator 跟 critic 共用 base model、訓練分佈中的盲點不會因重跑消失、loop 收斂到同樣錯誤）。判讀訊號：若 critic 每次給的修正建議都很像、或修完還是同一類錯、就是 systematic error、加 reflection steps 無收益。修法：換不同 base model 當 critic、或加外部驗證（test、lint、schema check）取代 LLM critic。Agent loop 是 reflection 的特例、進階失敗模式分析見 <a href="/blog/llm/04-applications/agent-architecture/" data-link-title="4.4 Agent 架構原理" data-link-desc="Agent loop 結構、失敗模式、什麼任務適合 vs 不適合、跟人類審查的協作模型">4.4 Agent 架構</a> 的三類失敗段。</p>
<p><strong>緩解策略</strong>：</p>
<ul>
<li>Critic 用不同模型、或不同 prompt 角度、減少 blind spot。</li>
<li>Reflection 設 step 上限、即使沒達標也強制停。</li>
<li>客觀驗證訊號（test pass、schema 合法、外部 metric）優先於 LLM critic 主觀評估。</li>
<li>Reflection 對有客觀訊號的任務 ROI 高、對純主觀偏好任務收益有限（critic 的「主觀好壞」跟 generator 同 base 時是同樣 distribution、無法 calibrate）。</li>
</ul>
<h2 id="四種模式的組合">四種模式的組合</h2>
<p>實際應用通常混用、不是純單一模式：</p>
<ul>
<li><strong>Pipeline of routers</strong>：第一步先 router 分類、每個 path 內部又是 pipeline。</li>
<li><strong>Parallel of pipelines</strong>：多個 pipeline 平行跑、最後合併。</li>
<li><strong>Reflection inside pipeline</strong>：pipeline 中某幾步用 reflection loop 改進、其他步驟 single call。</li>
<li><strong>Router into reflection</strong>：依輸入複雜度分流、簡單走 single call、複雜走 reflection loop。</li>
</ul>
<p>複雜應用通常是這四種模式的多層組合。看 LLM application 結構時、能識別出基本模式組合、就能判斷它的複雜度合不合理。</p>
<p>組合的判讀訊號：</p>
<ul>
<li>三層以上嵌套通常 over-engineered、考慮簡化。</li>
<li>同個模式重複用很多次（如 5 個 pipeline 串）可能拆得太細。</li>
<li>看不出基本模式的 ad-hoc 流程、通常維護困難。</li>
</ul>
<h2 id="何時退化成-single-call-更好">何時退化成 Single Call 更好</h2>
<p>判讀「該不該設計 workflow」的反射：先問「single call 能不能解」、不行再考慮拆。</p>
<p>Single call 更適合的訊號：</p>
<ul>
<li>上下文短（&lt; 8K token、塞得進現代 LLM）。</li>
<li>推理單純、不需要 multi-step。</li>
<li>不需要 tool 或只需一兩個簡單 tool。</li>
<li>沒有客觀驗證訊號可用、reflection 沒意義。</li>
<li>Latency 敏感、不能接受多次 round trip。</li>
</ul>
<p>「我先寫個 pipeline」常是過早優化：</p>
<ul>
<li>簡單問題切成 5 步、累積誤差大過拆分收益。</li>
<li>為了「靈活性」抽象太多、最終比 single prompt 還難改。</li>
<li>寫 workflow framework 的成本超過用 raw API 的成本。</li>
</ul>
<p>實務做法：先 single call baseline、跑半週看品質、不夠用再分解；workflow 設計留到 baseline 不足之後。</p>
<h2 id="workflow-設計常見的反模式">Workflow 設計常見的反模式</h2>
<p>幾種特別容易踩的反模式：</p>
<h3 id="過度切碎-pipeline">過度切碎 pipeline</h3>
<p>把任務切成 10 步、每步一個 LLM call、累積誤差大、latency 拖長、cost 爆。問題通常是「我以為拆細了品質會好」、實際相反。</p>
<p>訊號：pipeline 步驟超過 5 個、每步輸入輸出量級接近、看不出為什麼需要分。</p>
<p>緩解：能合併的合併、保留必要切點（中間有外部 tool 介入、或需要驗證的步驟）。</p>
<h3 id="parallel-跑根本不需要並行的事">Parallel 跑根本不需要並行的事</h3>
<p>兩個 call 其實依賴彼此、或本質是同個任務、硬要 parallel。Cost 是 sequential 的 N 倍、品質沒提升。</p>
<p>訊號：parallel 出來的結果合併邏輯複雜、或合併結果跟「直接 sequential 跑」差不多。</p>
<p>緩解：parallel 前問「這幾個 call 真的獨立、結果真的可合併嗎」、不獨立就 sequential。</p>
<h3 id="reflection-沒有客觀停止條件">Reflection 沒有客觀停止條件</h3>
<p>Reflection loop 純靠模型自己判斷「夠好了沒」、容易過度修正或無限循環。</p>
<p>訊號：reflection loop 沒有 step cap、沒有外部 metric、純依模型自評。</p>
<p>緩解：每個 reflection loop 都設 step 上限 + 客觀停止訊號（test pass、external check）。</p>
<h3 id="router-classifier-過於複雜">Router classifier 過於複雜</h3>
<p>Classifier 本身就需要強模型、變成 router「省 cost」反而花更多。</p>
<p>訊號：classifier 用的模型跟下游 path 同等級或更強。</p>
<p>緩解：classifier 用最便宜的小模型；最便宜小模型若 confidence 不夠、改成「沒有 router、全部走主 path」。</p>
<h3 id="看不出基本模式的-ad-hoc-流程">看不出基本模式的 ad-hoc 流程</h3>
<p>完全自訂的 control flow、不能對應到任何標準模式、維護困難。</p>
<p>訊號：流程圖畫不出來、新人接手要花一週搞懂、改一個 bug 影響不知道擴散到哪。</p>
<p>緩解：重新設計、強制套用基本模式組合。不能套用通常代表設計過度複雜。</p>
<h2 id="何時過時--何時不過時">何時過時 / 何時不過時</h2>
<p><strong>不會過時的部分</strong>：</p>
<ul>
<li>四種基本模式（pipeline / router / parallel / reflection）的結構跟失敗模式分類。</li>
<li>Multi-call 成本（latency / cost / 失敗點累乘）的本質。</li>
<li>「先 single call baseline、不夠再分解」的設計順序。</li>
<li>五個常見反模式的識別。</li>
</ul>
<p><strong>會變的部分</strong>：</p>
<ul>
<li>具體 workflow framework（LangGraph、LlamaIndex Workflow、各家 DAG runner）的 API。</li>
<li>「最佳化」的具體技巧（caching、batching、streaming 整合）。</li>
<li>哪些 framework 對哪種模式支援好（會持續演化）。</li>
</ul>
<p>看到新 workflow framework 時、回到本章四模式 framing、看它支援哪些模式、有沒有解決常見反模式、能不能跟你的應用場景對齊。Framework 換代不影響這四個模式的本質結構。</p>
<h2 id="下一章">下一章</h2>
<p>下一章：<a href="/blog/llm/04-applications/multi-agent-topology/" data-link-title="4.8 Multi-Agent 拓樸：flat / hierarchical / agent-as-tool" data-link-desc="從 multi-call workflow 走到 multi-agent system 的判讀、flat vs hierarchical 拓樸、agent-as-tool 的 MCP 視角、specialization 跟 orchestration overhead 的取捨">4.8 Multi-Agent 拓樸</a>、當 single-thread 多 call 不夠用、需要平行專業化角色 / 跨產品 agent 重用時、進入 multi-agent 系統的拓樸設計。</p>
<p>設計完 workflow 後、進 production 還要評估資源、latency / throughput 取捨、observability 三層、降級設計、見 <a href="/blog/llm/04-applications/production-resource-planning/" data-link-title="4.9 Production 部署的資源評估原理" data-link-desc="從本地單 user 到 production multi-tenant：concurrent users、cost model、observability、SLA、capacity planning 的設計取捨">4.9 Production 資源規劃</a>。</p>
]]></content:encoded></item><item><title>CI/CD 教學</title><link>https://tarrragon.github.io/blog/ci/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/</guid><description>&lt;p>CI/CD 教學的核心目標是把「變更如何被驗證、建置、交付」寫成可重播流程。&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/ci-pipeline/" data-link-title="CI Pipeline" data-link-desc="說明持續整合如何在合併前自動驗證變更品質與相容性">CI Pipeline&lt;/a> 負責驗證變更是否可信，&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/cd-pipeline/" data-link-title="CD Pipeline" data-link-desc="說明持續交付如何把已驗證產物推進到目標環境">CD Pipeline&lt;/a> 負責把可信 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">artifact&lt;/a> 交付到目標環境；兩者共享 gate、artifact、環境與回復路徑，但不同部署場域的細節差異很大。&lt;/p>
&lt;p>CI/CD 的責任是提供一致的判讀入口。當 workflow 顯示失敗時，團隊需要能快速判斷是 lint、test、build、package、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact Handoff&lt;/a>、deploy 還是 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy&lt;/a> 階段出問題，並知道下一步該回到本機重現、修正、重新提交，還是暫停發布。&lt;/p>
&lt;h3 id="前置知識卡片">&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/" data-link-title="Knowledge Cards" data-link-desc="用原子化卡片整理 CI/CD 章節的核心術語，讓流程文章專注在判讀與決策">前置知識卡片&lt;/a>&lt;/h3>
&lt;p>用原子化卡片整理 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">Artifact&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/required-checks/" data-link-title="Required Checks" data-link-desc="說明 pull request 的必要檢查如何作為合併 gate">Required Checks&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact Handoff&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">Environment Protection&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/preview-environment/" data-link-title="Preview Environment" data-link-desc="說明 pull request 變更如何在隔離部署環境中被驗證">Preview Environment&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout Strategy&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/image-digest/" data-link-title="Image Digest" data-link-desc="說明 container image digest 如何作為不可變產物身分，支撐掃描、推進與 runtime 追溯">Image Digest&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/release-channel/" data-link-title="Release Channel" data-link-desc="說明 stable、beta、internal 等發行通道如何控制 artifact 接觸到的使用者範圍">Release Channel&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/function-alias/" data-link-title="Function Alias" data-link-desc="說明 serverless function alias 如何把穩定入口指向特定版本並支援流量切換與回復">Function Alias&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/flaky-test/" data-link-title="Flaky Test" data-link-desc="說明非決定性測試如何降低 CI gate 信任度與治理方式">Flaky Test&lt;/a> 等核心術語。流程文章專注情境判讀與決策順序，術語背景交由卡片維持一致。&lt;/p>
&lt;h2 id="學習路線">學習路線&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>章節&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;th>核心責任&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="github-actions-failure-flow/">CI 失敗到修復發布流程&lt;/a>&lt;/td>
 &lt;td>Failure routing&lt;/td>
 &lt;td>從失敗 workflow 判斷下一步路由&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="ci-gate-workflow-boundary/">CI gate 與 workflow 邊界&lt;/a>&lt;/td>
 &lt;td>Workflow boundary&lt;/td>
 &lt;td>說明 required checks、needs 與 artifact handoff&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="frontend-deploy/">前端部署 CI/CD&lt;/a>&lt;/td>
 &lt;td>Frontend deployment&lt;/td>
 &lt;td>靜態站、SPA、CDN 與 preview environment&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="backend-deploy/">後端部署 CI/CD&lt;/a>&lt;/td>
 &lt;td>Backend deployment&lt;/td>
 &lt;td>API / worker 的 migration、rollout 與 rollback&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="app-deploy/">App 部署 CI/CD&lt;/a>&lt;/td>
 &lt;td>App deployment&lt;/td>
 &lt;td>mobile / desktop app 的簽章、審核與版本發布&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="docker-deploy/">Docker / Image 部署 CI/CD&lt;/a>&lt;/td>
 &lt;td>Image deployment&lt;/td>
 &lt;td>image build、scan、tag、registry 與 runtime&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="serverless-deploy/">Serverless 部署 CI/CD&lt;/a>&lt;/td>
 &lt;td>Serverless deployment&lt;/td>
 &lt;td>function 版本、權限、事件觸發與 alias rollback&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="data-pipeline-deploy/">Data Pipeline 部署 CI/CD&lt;/a>&lt;/td>
 &lt;td>Data pipeline deployment&lt;/td>
 &lt;td>schema 相容、backfill、checkpoint 與 rerun&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="iac-platform-deploy/">IaC / Platform 部署 CI/CD&lt;/a>&lt;/td>
 &lt;td>IaC deployment&lt;/td>
 &lt;td>plan/apply、drift、state 與環境治理&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="desktop-client-deploy/">Desktop Client 部署 CI/CD&lt;/a>&lt;/td>
 &lt;td>Desktop client deployment&lt;/td>
 &lt;td>桌面安裝包簽章、公證、更新通道與回退&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="package-library-release/">Package / Library Release CI/CD&lt;/a>&lt;/td>
 &lt;td>Package release deployment&lt;/td>
 &lt;td>SDK / NPM / PyPI 的版本、契約與發版供應鏈治理&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="blog-project-deploy/">本 blog 專案部署&lt;/a>&lt;/td>
 &lt;td>Project case&lt;/td>
 &lt;td>Hugo、Pagefind、GitHub Pages 與本專案 workflow&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="artifact-reproducibility/">Artifact 與可重播性&lt;/a>&lt;/td>
 &lt;td>Artifact reproducibility&lt;/td>
 &lt;td>讓 CI 產物能被測試與發布共用&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="flaky-test-governance/">Flaky test 治理&lt;/a>&lt;/td>
 &lt;td>Flaky governance&lt;/td>
 &lt;td>把不穩定測試從雜訊變成可處理任務&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>學習路線先從失敗處理與 gate 邊界開始，因為 CI/CD 的價值會在紅燈時最清楚。當讀者能判讀失敗位置與下一步路由，再依部署場域進入前端、後端、App、Docker 或本 blog 專案案例。&lt;/p></description><content:encoded><![CDATA[<p>CI/CD 教學的核心目標是把「變更如何被驗證、建置、交付」寫成可重播流程。<a href="/blog/ci/knowledge-cards/ci-pipeline/" data-link-title="CI Pipeline" data-link-desc="說明持續整合如何在合併前自動驗證變更品質與相容性">CI Pipeline</a> 負責驗證變更是否可信，<a href="/blog/ci/knowledge-cards/cd-pipeline/" data-link-title="CD Pipeline" data-link-desc="說明持續交付如何把已驗證產物推進到目標環境">CD Pipeline</a> 負責把可信 <a href="/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">artifact</a> 交付到目標環境；兩者共享 gate、artifact、環境與回復路徑，但不同部署場域的細節差異很大。</p>
<p>CI/CD 的責任是提供一致的判讀入口。當 workflow 顯示失敗時，團隊需要能快速判斷是 lint、test、build、package、<a href="/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact Handoff</a>、deploy 還是 <a href="/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy</a> 階段出問題，並知道下一步該回到本機重現、修正、重新提交，還是暫停發布。</p>
<h3 id="前置知識卡片"><a href="/blog/ci/knowledge-cards/" data-link-title="Knowledge Cards" data-link-desc="用原子化卡片整理 CI/CD 章節的核心術語，讓流程文章專注在判讀與決策">前置知識卡片</a></h3>
<p>用原子化卡片整理 <a href="/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">Artifact</a>、<a href="/blog/ci/knowledge-cards/required-checks/" data-link-title="Required Checks" data-link-desc="說明 pull request 的必要檢查如何作為合併 gate">Required Checks</a>、<a href="/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact Handoff</a>、<a href="/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">Environment Protection</a>、<a href="/blog/ci/knowledge-cards/preview-environment/" data-link-title="Preview Environment" data-link-desc="說明 pull request 變更如何在隔離部署環境中被驗證">Preview Environment</a>、<a href="/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout Strategy</a>、<a href="/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy</a>、<a href="/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration</a>、<a href="/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill</a>、<a href="/blog/ci/knowledge-cards/image-digest/" data-link-title="Image Digest" data-link-desc="說明 container image digest 如何作為不可變產物身分，支撐掃描、推進與 runtime 追溯">Image Digest</a>、<a href="/blog/ci/knowledge-cards/release-channel/" data-link-title="Release Channel" data-link-desc="說明 stable、beta、internal 等發行通道如何控制 artifact 接觸到的使用者範圍">Release Channel</a>、<a href="/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift</a>、<a href="/blog/ci/knowledge-cards/function-alias/" data-link-title="Function Alias" data-link-desc="說明 serverless function alias 如何把穩定入口指向特定版本並支援流量切換與回復">Function Alias</a> 與 <a href="/blog/ci/knowledge-cards/flaky-test/" data-link-title="Flaky Test" data-link-desc="說明非決定性測試如何降低 CI gate 信任度與治理方式">Flaky Test</a> 等核心術語。流程文章專注情境判讀與決策順序，術語背景交由卡片維持一致。</p>
<h2 id="學習路線">學習路線</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="github-actions-failure-flow/">CI 失敗到修復發布流程</a></td>
          <td>Failure routing</td>
          <td>從失敗 workflow 判斷下一步路由</td>
      </tr>
      <tr>
          <td><a href="ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a></td>
          <td>Workflow boundary</td>
          <td>說明 required checks、needs 與 artifact handoff</td>
      </tr>
      <tr>
          <td><a href="frontend-deploy/">前端部署 CI/CD</a></td>
          <td>Frontend deployment</td>
          <td>靜態站、SPA、CDN 與 preview environment</td>
      </tr>
      <tr>
          <td><a href="backend-deploy/">後端部署 CI/CD</a></td>
          <td>Backend deployment</td>
          <td>API / worker 的 migration、rollout 與 rollback</td>
      </tr>
      <tr>
          <td><a href="app-deploy/">App 部署 CI/CD</a></td>
          <td>App deployment</td>
          <td>mobile / desktop app 的簽章、審核與版本發布</td>
      </tr>
      <tr>
          <td><a href="docker-deploy/">Docker / Image 部署 CI/CD</a></td>
          <td>Image deployment</td>
          <td>image build、scan、tag、registry 與 runtime</td>
      </tr>
      <tr>
          <td><a href="serverless-deploy/">Serverless 部署 CI/CD</a></td>
          <td>Serverless deployment</td>
          <td>function 版本、權限、事件觸發與 alias rollback</td>
      </tr>
      <tr>
          <td><a href="data-pipeline-deploy/">Data Pipeline 部署 CI/CD</a></td>
          <td>Data pipeline deployment</td>
          <td>schema 相容、backfill、checkpoint 與 rerun</td>
      </tr>
      <tr>
          <td><a href="iac-platform-deploy/">IaC / Platform 部署 CI/CD</a></td>
          <td>IaC deployment</td>
          <td>plan/apply、drift、state 與環境治理</td>
      </tr>
      <tr>
          <td><a href="desktop-client-deploy/">Desktop Client 部署 CI/CD</a></td>
          <td>Desktop client deployment</td>
          <td>桌面安裝包簽章、公證、更新通道與回退</td>
      </tr>
      <tr>
          <td><a href="package-library-release/">Package / Library Release CI/CD</a></td>
          <td>Package release deployment</td>
          <td>SDK / NPM / PyPI 的版本、契約與發版供應鏈治理</td>
      </tr>
      <tr>
          <td><a href="blog-project-deploy/">本 blog 專案部署</a></td>
          <td>Project case</td>
          <td>Hugo、Pagefind、GitHub Pages 與本專案 workflow</td>
      </tr>
      <tr>
          <td><a href="artifact-reproducibility/">Artifact 與可重播性</a></td>
          <td>Artifact reproducibility</td>
          <td>讓 CI 產物能被測試與發布共用</td>
      </tr>
      <tr>
          <td><a href="flaky-test-governance/">Flaky test 治理</a></td>
          <td>Flaky governance</td>
          <td>把不穩定測試從雜訊變成可處理任務</td>
      </tr>
  </tbody>
</table>
<p>學習路線先從失敗處理與 gate 邊界開始，因為 CI/CD 的價值會在紅燈時最清楚。當讀者能判讀失敗位置與下一步路由，再依部署場域進入前端、後端、App、Docker 或本 blog 專案案例。</p>
<h2 id="與其他教學的分工">與其他教學的分工</h2>
<p>CI/CD 教學負責日常工作流程與部署場域差異，Backend 可靠性模組負責系統層可靠性判斷。讀者想知道 workflow 失敗後怎麼修、發布 gate 怎麼切、前端與後端部署流程差在哪裡，讀本系列；想知道 CI 在 release gate、SLO、load test 與可靠性治理中的位置，回到 <a href="/blog/backend/06-reliability/" data-link-title="模組六：可靠性驗證流程" data-link-desc="用 SRE 領域詞彙建問題節點、以服務級案例庫累積驗證脈絡，先建概念與案例庫再進實作交接">模組六：可靠性驗證流程</a>。</p>
<p>Go、Python 或其他語言教材只需要保留測試寫法與本機命令。當內容開始涉及 workflow event、required checks、preview deployment、container registry、mobile signing、artifact、cache 或 branch protection，就應該移到本系列，讓不同語言共用同一套 CI/CD 操作語意。</p>
<h2 id="判讀訊號">判讀訊號</h2>
<ul>
<li>GitHub Actions 紅燈後，不知道該看哪個 job。</li>
<li>本機測試通過，但 CI 失敗。</li>
<li>測試失敗後仍有部署 workflow 啟動。</li>
<li>deploy 失敗時，團隊分不清 build artifact、部署權限與測試 gate 的責任。</li>
<li>前端、後端、App 與 Docker 使用同一套發布說明，導致場域細節混在一起。</li>
<li>workflow 只有命令清單，沒有說明失敗後的處理路由與部署場域邊界。</li>
</ul>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>想處理 GitHub Actions 紅燈：讀 <a href="github-actions-failure-flow/">CI 失敗到修復發布流程</a>。</li>
<li>想理解 CI gate 原理：讀 <a href="ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
<li>想理解前端部署：讀 <a href="frontend-deploy/">前端部署 CI/CD</a>。</li>
<li>想理解後端部署：讀 <a href="backend-deploy/">後端部署 CI/CD</a>。</li>
<li>想理解 App 發布：讀 <a href="app-deploy/">App 部署 CI/CD</a>。</li>
<li>想理解 Docker / image 流程：讀 <a href="docker-deploy/">Docker / Image 部署 CI/CD</a>。</li>
<li>想理解 Serverless 發布：讀 <a href="serverless-deploy/">Serverless 部署 CI/CD</a>。</li>
<li>想理解資料處理任務發布：讀 <a href="data-pipeline-deploy/">Data Pipeline 部署 CI/CD</a>。</li>
<li>想理解 IaC / 平台變更發布：讀 <a href="iac-platform-deploy/">IaC / Platform 部署 CI/CD</a>。</li>
<li>想理解 Flutter/Electron/Tauri 類客戶端發布：讀 <a href="desktop-client-deploy/">Desktop Client 部署 CI/CD</a>。</li>
<li>想理解 SDK / NPM / PyPI 發版：讀 <a href="package-library-release/">Package / Library Release CI/CD</a>。</li>
<li>想維護本 blog 的 workflow：讀 <a href="blog-project-deploy/">本 blog 專案部署</a>。</li>
<li>想讓測試與發布共用同一份產物：讀 <a href="artifact-reproducibility/">Artifact 與可重播性</a>。</li>
<li>想治理不穩定測試：讀 <a href="flaky-test-governance/">Flaky test 治理</a>。</li>
<li>想理解可靠性層的 CI 分層：讀 <a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">6.1 CI pipeline</a>。</li>
<li>想理解發布 gate：讀 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate 與變更節奏</a>。</li>
<li>想理解 infra 變更的 plan / apply 流程怎麼走 CI：讀 <a href="/blog/infra/07-infra-as-pr/" data-link-title="模組七：infra 走 PR 流程與自動化護欄" data-link-desc="infra 變更走 PR → plan → review diff → 合併 → apply，配 fmt / validate / tflint / checkov / tfsec 與 Atlantis 自動化，讓基礎設施可審查、可回溯、可交接">Infra 走 PR 流程與自動化護欄</a>。</li>
</ul>
]]></content:encoded></item><item><title>模組零：Dotfile 心智模型</title><link>https://tarrragon.github.io/blog/linux/dotfile/00-dotfile-mindset/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/00-dotfile-mindset/</guid><description>&lt;p>Dotfile 管理的核心能力是&lt;strong>環境可重現性&lt;/strong>：把個人開發環境的配置狀態變成版控下的代碼，讓任何一台空白機器都能用一份 Git repo 還原成你熟悉的工作桌面。&lt;/p>
&lt;p>Unix 系統用檔名開頭的 &lt;code>.&lt;/code> 標記隱藏檔。shell 配置（&lt;code>.bashrc&lt;/code>、&lt;code>.zshrc&lt;/code>）、Git 設定（&lt;code>.gitconfig&lt;/code>）、SSH 設定（&lt;code>.ssh/config&lt;/code>）、以及 &lt;code>~/.config/&lt;/code> 底下各種工具的配置目錄，都屬於這個範疇。「dotfile 管理」指的是把這些散落在家目錄各處的配置檔集中到一個 Git repo，建立版本歷史、可以跨機器同步、可以在新環境一鍵部署。&lt;/p>
&lt;h2 id="章節文章">章節文章&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>文章&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/00-dotfile-mindset/environment-reproducibility/" data-link-title="環境可重現性與配置分類" data-link-desc="想釐清哪些配置該進 dotfile repo、哪些不該進時回來讀">環境可重現性與配置分類&lt;/a>&lt;/td>
 &lt;td>為什麼要管理 dotfile、哪些東西該進 repo、核心層 / 工具層 / 桌面層的分類判讀&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/00-dotfile-mindset/dotfile-iac-parallel/" data-link-title="Dotfile 跟 Infra IaC 的平行關係" data-link-desc="想理解 dotfile 管理在工程實踐裡的定位、或釐清「重建指令」跟「備份」的差異時回來讀">Dotfile 跟 Infra IaC 的平行關係&lt;/a>&lt;/td>
 &lt;td>兩者共用的原則與差異、「重建指令不是備份」的心智模型&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/00-dotfile-mindset/setup-order-guide/" data-link-title="環境建置的操作順序" data-link-desc="第一次從零建立 Linux 或 macOS 開發環境、不確定先做什麼後做什麼時讀 — 依賴順序路線圖，每一步附對應模組連結">環境建置的操作順序&lt;/a>&lt;/td>
 &lt;td>第一次建環境時先做什麼後做什麼——依賴順序路線圖，每步附對應模組連結&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="跨分類引用">跨分類引用&lt;/h2>
&lt;ul>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">模組一：管理工具與目錄結構&lt;/a>：怎麼把散落的配置檔收進 Git repo&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">模組八：同步、Bootstrap 與環境重建&lt;/a>：環境重建的完整流程&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/09-team-environment/" data-link-title="模組九：從個人到團隊" data-link-desc="個人 dotfile 管理的思想要延伸到團隊開發環境標準化時回來讀 — devcontainer、nix、商業環境配置管理">模組九：從個人到團隊&lt;/a>：dotfile 思想往團隊環境延伸&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/infra/" data-link-title="Infra 基礎設施建置指南" data-link-desc="從零循序漸進把雲端基礎設施做起來 — IaC、身分憑證、網路地基、環境分離、核心服務、可觀測性、自動化 review 與治理習慣，含怎麼在組織內推動">Infra 基礎設施建置指南&lt;/a>：組織層級的環境 as code&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Dotfile 管理的核心能力是<strong>環境可重現性</strong>：把個人開發環境的配置狀態變成版控下的代碼，讓任何一台空白機器都能用一份 Git repo 還原成你熟悉的工作桌面。</p>
<p>Unix 系統用檔名開頭的 <code>.</code> 標記隱藏檔。shell 配置（<code>.bashrc</code>、<code>.zshrc</code>）、Git 設定（<code>.gitconfig</code>）、SSH 設定（<code>.ssh/config</code>）、以及 <code>~/.config/</code> 底下各種工具的配置目錄，都屬於這個範疇。「dotfile 管理」指的是把這些散落在家目錄各處的配置檔集中到一個 Git repo，建立版本歷史、可以跨機器同步、可以在新環境一鍵部署。</p>
<h2 id="章節文章">章節文章</h2>
<table>
  <thead>
      <tr>
          <th>文章</th>
          <th>主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/linux/dotfile/00-dotfile-mindset/environment-reproducibility/" data-link-title="環境可重現性與配置分類" data-link-desc="想釐清哪些配置該進 dotfile repo、哪些不該進時回來讀">環境可重現性與配置分類</a></td>
          <td>為什麼要管理 dotfile、哪些東西該進 repo、核心層 / 工具層 / 桌面層的分類判讀</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/00-dotfile-mindset/dotfile-iac-parallel/" data-link-title="Dotfile 跟 Infra IaC 的平行關係" data-link-desc="想理解 dotfile 管理在工程實踐裡的定位、或釐清「重建指令」跟「備份」的差異時回來讀">Dotfile 跟 Infra IaC 的平行關係</a></td>
          <td>兩者共用的原則與差異、「重建指令不是備份」的心智模型</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/00-dotfile-mindset/setup-order-guide/" data-link-title="環境建置的操作順序" data-link-desc="第一次從零建立 Linux 或 macOS 開發環境、不確定先做什麼後做什麼時讀 — 依賴順序路線圖，每一步附對應模組連結">環境建置的操作順序</a></td>
          <td>第一次建環境時先做什麼後做什麼——依賴順序路線圖，每步附對應模組連結</td>
      </tr>
  </tbody>
</table>
<h2 id="跨分類引用">跨分類引用</h2>
<ul>
<li>→ <a href="/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">模組一：管理工具與目錄結構</a>：怎麼把散落的配置檔收進 Git repo</li>
<li>→ <a href="/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">模組八：同步、Bootstrap 與環境重建</a>：環境重建的完整流程</li>
<li>→ <a href="/blog/linux/dotfile/09-team-environment/" data-link-title="模組九：從個人到團隊" data-link-desc="個人 dotfile 管理的思想要延伸到團隊開發環境標準化時回來讀 — devcontainer、nix、商業環境配置管理">模組九：從個人到團隊</a>：dotfile 思想往團隊環境延伸</li>
<li>→ <a href="/blog/infra/" data-link-title="Infra 基礎設施建置指南" data-link-desc="從零循序漸進把雲端基礎設施做起來 — IaC、身分憑證、網路地基、環境分離、核心服務、可觀測性、自動化 review 與治理習慣，含怎麼在組織內推動">Infra 基礎設施建置指南</a>：組織層級的環境 as code</li>
</ul>
]]></content:encoded></item></channel></rss>