<?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>Preview on Tarragon</title><link>https://tarrragon.github.io/blog/tags/preview/</link><description>Recent content in Preview on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Thu, 21 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/preview/index.xml" rel="self" type="application/rss+xml"/><item><title>前端 artifact 與 preview deployment 流程</title><link>https://tarrragon.github.io/blog/ci/frontend-deploy/static-artifact-preview-flow/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/frontend-deploy/static-artifact-preview-flow/</guid><description>&lt;p>前端 artifact 流程的核心責任是讓測試、預覽與正式發布使用同一份靜態產物。前端部署常見輸出是 HTML、CSS、JavaScript、圖片、sourcemap 與搜尋索引；這些產物一旦被重新 build，就可能受到環境變數、依賴版本、base URL 或 framework 設定影響，因此 CI/CD 需要把「產生一次、驗證一次、推進同一份」當成主線。&lt;/p>
&lt;h2 id="流程定位">流程定位&lt;/h2>
&lt;p>前端部署的風險集中在 build time。後端服務可以在 runtime 讀取設定、檢查資料庫與逐步接流量；前端靜態產物多半在 build 階段就把 route、asset path、環境變數與 feature flag 預先寫入 bundle。CI/CD 的判讀重點因此是「被部署的 artifact 是否就是已驗證的那一份」。&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>Build&lt;/td>
 &lt;td>產生 production-like static artifact&lt;/td>
 &lt;td>lockfile、Node 版本、base URL 是否固定&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Browser test&lt;/td>
 &lt;td>驗證使用者可見行為&lt;/td>
 &lt;td>測試是否跑在 build 後 artifact&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&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;/td>
 &lt;td>讓 PR 變更可被 reviewer 實際操作&lt;/td>
 &lt;td>preview URL 是否對應 commit / PR&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Deploy&lt;/td>
 &lt;td>推進到 hosting、Pages 或 CDN&lt;/td>
 &lt;td>HTML cache、asset cache、SPA fallback&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&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;/td>
 &lt;td>重新服務上一份已知可用 artifact&lt;/td>
 &lt;td>舊 artifact、cache purge 與 API 相容性&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Build 階段負責建立可驗證產物。真實服務裡，&lt;code>npm run dev&lt;/code> 成功不代表 production build 能成功；CI 應固定 Node 版本、package manager、lockfile、build command 與必要環境變數，讓 artifact 可以從乾淨環境重建。&lt;/p>
&lt;p>Browser test 階段負責驗證使用者實際會看到的頁面。Playwright、visual diff、a11y check 或 smoke test 應盡量對 build 後的靜態站執行，避免 dev server 的 fallback、熱更新或寬鬆路由遮蔽 production 問題。&lt;/p>
&lt;p>&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> 階段負責把 PR 變成可操作畫面。Preview URL 要能追到 PR、commit 與 workflow run，reviewer 才能把畫面問題回報到正確版本；preview 也要隔離 production 資料與 credential，避免預覽環境變成未受控入口。&lt;/p>
&lt;p>Deploy 階段負責把 artifact 放到 hosting 或 CDN。前端部署失敗常出現在 cache policy、SPA fallback、base URL、static route 與 sourcemap 權限；deploy 成功只代表檔案上傳完成，仍需要檢查入口頁、核心路由與 asset 是否能從公開網址載入。&lt;/p>
&lt;p>&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> 階段負責恢復上一份可用靜態產物。前端 rollback 表面上只是切回舊檔案，但若 API schema、build time config 或 CDN cache 已經變動，舊頁面仍可能呼叫不相容的後端，因此 rollback 要搭配 smoke test 與 cache purge。&lt;/p>
&lt;h2 id="常見失敗路由">常見失敗路由&lt;/h2>
&lt;p>前端 CI 紅燈要先判斷失敗在 build、browser test、preview 還是 production deploy。不同層的修復入口不同；把所有紅燈都當成「重跑 workflow」會掩蓋 artifact 漂移與 cache 問題。&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>本機 dev 正常、CI build 失敗&lt;/td>
 &lt;td>production build 條件與本機不同&lt;/td>
 &lt;td>回本機跑 CI 同一條 build command&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>測試通過、上線後空白頁&lt;/td>
 &lt;td>測試沒有覆蓋 production artifact / URL&lt;/td>
 &lt;td>對已部署 artifact 跑 smoke test&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Preview URL 顯示舊畫面&lt;/td>
 &lt;td>preview cache 或 commit 對應錯位&lt;/td>
 &lt;td>檢查 preview artifact 與 workflow run&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>只有深層路由 404&lt;/td>
 &lt;td>SPA fallback 或 static route 設定錯誤&lt;/td>
 &lt;td>檢查 hosting rewrite / base URL&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>rollback 後仍看到新版&lt;/td>
 &lt;td>CDN / browser cache 尚未失效&lt;/td>
 &lt;td>檢查 cache invalidation 與 HTML cache policy&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這張表的用途是縮短定位時間。前端部署問題常被誤判成「CDN 壞掉」或「瀏覽器快取」，但更常見的根因是 build artifact、route 與 cache policy 的契約沒有明確寫進 pipeline。&lt;/p></description><content:encoded><![CDATA[<p>前端 artifact 流程的核心責任是讓測試、預覽與正式發布使用同一份靜態產物。前端部署常見輸出是 HTML、CSS、JavaScript、圖片、sourcemap 與搜尋索引；這些產物一旦被重新 build，就可能受到環境變數、依賴版本、base URL 或 framework 設定影響，因此 CI/CD 需要把「產生一次、驗證一次、推進同一份」當成主線。</p>
<h2 id="流程定位">流程定位</h2>
<p>前端部署的風險集中在 build time。後端服務可以在 runtime 讀取設定、檢查資料庫與逐步接流量；前端靜態產物多半在 build 階段就把 route、asset path、環境變數與 feature flag 預先寫入 bundle。CI/CD 的判讀重點因此是「被部署的 artifact 是否就是已驗證的那一份」。</p>
<table>
  <thead>
      <tr>
          <th>階段</th>
          <th>責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build</td>
          <td>產生 production-like static artifact</td>
          <td>lockfile、Node 版本、base URL 是否固定</td>
      </tr>
      <tr>
          <td>Browser test</td>
          <td>驗證使用者可見行為</td>
          <td>測試是否跑在 build 後 artifact</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/preview-environment/" data-link-title="Preview Environment" data-link-desc="說明 pull request 變更如何在隔離部署環境中被驗證">Preview environment</a></td>
          <td>讓 PR 變更可被 reviewer 實際操作</td>
          <td>preview URL 是否對應 commit / PR</td>
      </tr>
      <tr>
          <td>Deploy</td>
          <td>推進到 hosting、Pages 或 CDN</td>
          <td>HTML cache、asset cache、SPA fallback</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback strategy</a></td>
          <td>重新服務上一份已知可用 artifact</td>
          <td>舊 artifact、cache purge 與 API 相容性</td>
      </tr>
  </tbody>
</table>
<p>Build 階段負責建立可驗證產物。真實服務裡，<code>npm run dev</code> 成功不代表 production build 能成功；CI 應固定 Node 版本、package manager、lockfile、build command 與必要環境變數，讓 artifact 可以從乾淨環境重建。</p>
<p>Browser test 階段負責驗證使用者實際會看到的頁面。Playwright、visual diff、a11y check 或 smoke test 應盡量對 build 後的靜態站執行，避免 dev server 的 fallback、熱更新或寬鬆路由遮蔽 production 問題。</p>
<p><a href="/blog/ci/knowledge-cards/preview-environment/" data-link-title="Preview Environment" data-link-desc="說明 pull request 變更如何在隔離部署環境中被驗證">Preview environment</a> 階段負責把 PR 變成可操作畫面。Preview URL 要能追到 PR、commit 與 workflow run，reviewer 才能把畫面問題回報到正確版本；preview 也要隔離 production 資料與 credential，避免預覽環境變成未受控入口。</p>
<p>Deploy 階段負責把 artifact 放到 hosting 或 CDN。前端部署失敗常出現在 cache policy、SPA fallback、base URL、static route 與 sourcemap 權限；deploy 成功只代表檔案上傳完成，仍需要檢查入口頁、核心路由與 asset 是否能從公開網址載入。</p>
<p><a href="/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback strategy</a> 階段負責恢復上一份可用靜態產物。前端 rollback 表面上只是切回舊檔案，但若 API schema、build time config 或 CDN cache 已經變動，舊頁面仍可能呼叫不相容的後端，因此 rollback 要搭配 smoke test 與 cache purge。</p>
<h2 id="常見失敗路由">常見失敗路由</h2>
<p>前端 CI 紅燈要先判斷失敗在 build、browser test、preview 還是 production deploy。不同層的修復入口不同；把所有紅燈都當成「重跑 workflow」會掩蓋 artifact 漂移與 cache 問題。</p>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀</th>
          <th>下一步</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>本機 dev 正常、CI build 失敗</td>
          <td>production build 條件與本機不同</td>
          <td>回本機跑 CI 同一條 build command</td>
      </tr>
      <tr>
          <td>測試通過、上線後空白頁</td>
          <td>測試沒有覆蓋 production artifact / URL</td>
          <td>對已部署 artifact 跑 smoke test</td>
      </tr>
      <tr>
          <td>Preview URL 顯示舊畫面</td>
          <td>preview cache 或 commit 對應錯位</td>
          <td>檢查 preview artifact 與 workflow run</td>
      </tr>
      <tr>
          <td>只有深層路由 404</td>
          <td>SPA fallback 或 static route 設定錯誤</td>
          <td>檢查 hosting rewrite / base URL</td>
      </tr>
      <tr>
          <td>rollback 後仍看到新版</td>
          <td>CDN / browser cache 尚未失效</td>
          <td>檢查 cache invalidation 與 HTML cache policy</td>
      </tr>
  </tbody>
</table>
<p>這張表的用途是縮短定位時間。前端部署問題常被誤判成「CDN 壞掉」或「瀏覽器快取」，但更常見的根因是 build artifact、route 與 cache policy 的契約沒有明確寫進 pipeline。</p>
<h2 id="最小-workflow-骨架">最小 workflow 骨架</h2>
<p>前端 workflow 應把 build、test、preview 與 deploy 的資料流顯性化。下面是概念骨架，重點在 artifact handoff 的方向，特定平台語法是次要的。</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">jobs</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">build</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">steps</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">run</span><span class="p">:</span><span class="w"> </span><span class="l">npm ci</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">run</span><span class="p">:</span><span class="w"> </span><span class="l">npm run build</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">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/upload-artifact</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">with</span><span class="p">:</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">name</span><span class="p">:</span><span class="w"> </span><span class="l">web-dist</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">path</span><span class="p">:</span><span class="w"> </span><span class="l">dist</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">  </span><span class="nt">test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="l">build</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/download-artifact</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">web-dist</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">      </span>- <span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">npm run test:e2e:static</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">  </span><span class="nt">preview</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">    </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="l">test</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">    </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">github.event_name == &#39;pull_request&#39;</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/download-artifact</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">web-dist</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">      </span>- <span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">npm run deploy:preview</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">  </span><span class="nt">deploy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">    </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="l">test</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">    </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">github.ref == &#39;refs/heads/main&#39;</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/download-artifact</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">web-dist</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">      </span>- <span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">npm run deploy:production</span></span></span></code></pre></div><p>這個骨架讓 deploy 依賴 test，也讓 test 與 deploy 使用 build job 產生的同一份產物。若專案需要在不同環境注入設定，要明確區分 build time config 與 runtime config，避免同一份 artifact 被重新 build 成另一份內容。</p>
<h2 id="tripwire">Tripwire</h2>
<p>Tripwire 的責任是提醒前端 workflow 需要重切。當同一類問題反覆出現，局部補命令通常只能暫時遮住資料流錯位。</p>
<ul>
<li>Preview 常和 production 不一致：把 preview 改成部署 build artifact，讓 preview job 沿用同一份產物。</li>
<li>E2E 測試通過但 production 壞：把 E2E 改到 static artifact 或 production-like server 上執行。</li>
<li>rollback 依賴人工找舊 commit：保留 release artifact 與版本索引，讓回退指向明確產物。</li>
<li>CDN cache 問題反覆出現：把 HTML cache、asset cache 與 purge 策略寫進 deploy checklist。</li>
</ul>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>前端部署總覽：回 <a href="../">前端部署 CI/CD</a>。</li>
<li>Gate 原理：讀 <a href="../../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
<li>本 blog 靜態站案例：讀 <a href="../../blog-project-deploy/">本 blog 專案部署</a>。</li>
</ul>
]]></content:encoded></item><item><title>Preview Environment</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/preview-environment/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/preview-environment/</guid><description>&lt;p>Preview Environment 的核心概念是「在合併前提供接近正式環境的可驗證入口」。它把 code review 從靜態 diff 延伸到真實互動行為。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Preview Environment 位在 pull request workflow 與正式部署流程之間，常由臨時 URL、隔離資源與到期清理組成。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>團隊需要在合併前驗證 UI、路由或互動行為。&lt;/li>
&lt;li>單靠測試報告不足以判斷體驗差異。&lt;/li>
&lt;li>變更常包含環境變數、CDN 設定或靜態資產路徑。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>前端 PR 自動建 preview URL 給 reviewer 驗證。後端則可能建立 review app 供 API 與整合測試使用。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Preview Environment 要定義建立條件、資源上限、可見範圍與清理策略，避免成本與風險失控。&lt;/p></description><content:encoded><![CDATA[<p>Preview Environment 的核心概念是「在合併前提供接近正式環境的可驗證入口」。它把 code review 從靜態 diff 延伸到真實互動行為。</p>
<h2 id="概念位置">概念位置</h2>
<p>Preview Environment 位在 pull request workflow 與正式部署流程之間，常由臨時 URL、隔離資源與到期清理組成。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>團隊需要在合併前驗證 UI、路由或互動行為。</li>
<li>單靠測試報告不足以判斷體驗差異。</li>
<li>變更常包含環境變數、CDN 設定或靜態資產路徑。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>前端 PR 自動建 preview URL 給 reviewer 驗證。後端則可能建立 review app 供 API 與整合測試使用。</p>
<h2 id="設計責任">設計責任</h2>
<p>Preview Environment 要定義建立條件、資源上限、可見範圍與清理策略，避免成本與風險失控。</p>
]]></content:encoded></item></channel></rss>