CI/CD 白話拆解:一次 git push 之後,機器幫我做了什麼

我網站的每次更新都是 git push 完就自動上線。這篇把背後的 CI/CD 流程一行一行拆給非本科看,並示範怎麼自己從零設一個。

#ci-cd#github-actions#cloudflare#自動化部署#教學

重點摘要

CI/CD 聽起來很像一個要另外學的大題目,但它的本質很簡單:「當我把程式碼推上去,就自動幫我建置好、上線」的一條流水線。這個網站就是這樣運作的 —— 我 git push 之後不碰任何東西,2–3 分鐘後新版自己出現在網路上。這篇用這個網站真實的設定檔,一行一行拆給沒接觸過的人看,並示範怎麼自己設一條。


先建立心智模型

沒有 CI/CD 的時候,手動上線一個網站要做這些事:

  1. 在自己電腦上把網站「建置」成瀏覽器看得懂的檔案
  2. 把建好的檔案上傳到伺服器
  3. 每次改一點,就重複 1–2

CI/CD 做的事,就是把 1–2 交給一台雲端的機器,觸發條件是「你推了新程式碼」。整條流水線可以拆成四個角色:

觸發(Trigger)      ── 什麼事件會啟動流水線?(這裡:push 到 main 分支)

跑手(Runner)       ── 一台雲端臨時電腦,乾淨環境從頭跑

步驟(Steps)        ── 拿程式碼 → 裝相依套件 → 建置 → 部署

部署(Deploy)       ── 把成品送到託管平台,網站更新

記住這四個字:觸發 → 跑手 → 步驟 → 部署。下面每一段都對應其中一塊。


我用的工具

  • GitHub Actions:負責「觸發 + 跑手 + 步驟」。它讀 repo 裡一個設定檔,照著跑。
  • Cloudflare Pages:負責「部署 + 託管」。最後成品放在這裡對外服務。

設定檔放在 repo 的固定位置:.github/workflows/deploy.yml。GitHub 看到這個資料夾裡的 YAML 檔,就知道要跑流水線。


一行一行拆這個設定檔

以下就是這個網站實際在用的 deploy.yml(略去註解)。別怕,拆開來每塊都很小:

name: Deploy to Cloudflare Pages

on:
  push:
    branches: [main]
  workflow_dispatch:

這是「觸發」段。 on: 說明什麼時候啟動:

  • pushmain 分支 → 每次我把改動推上主線,就跑。
  • workflow_dispatch → 允許我在 GitHub 網頁上手動按一下也能跑(不推程式也能重跑)。
jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      deployments: write

這是「跑手」段。 runs-on: ubuntu-latest = 跟 GitHub 借一台跑 Ubuntu Linux 的臨時乾淨電腦。它每次都是全新的,跑完就丟 —— 這一點很重要,代表「在我電腦上跑得動、在這裡卻失敗」通常是因為我電腦有裝、但乾淨環境沒有的東西。permissions 是給這台機器最小夠用的權限(能讀程式碼、能寫部署紀錄),多的不給。

    steps:
      - name: Checkout
        uses: actions/checkout@v4

步驟 1:拿程式碼。 那台臨時電腦一開始是空的,這步把我的 repo 內容抓下來。uses: 代表「用別人寫好的現成動作」,@v4 是版本。

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '22'
          cache: 'npm'

步驟 2:裝好 Node.js 指定版本 22(跟我本機一致,避免版本落差出事)。cache: 'npm' 會把相依套件快取起來,下次跑更快。

      - name: Install dependencies
        run: npm ci

步驟 3:裝相依套件。 run: 代表「在這台機器的命令列執行這行指令」。npm ci 是照 package-lock.json 精準安裝(比 npm install 更適合自動化,因為它保證每次裝到一模一樣的版本)。

      - name: Build site
        run: npm run build

步驟 4:建置。 把原始碼編譯成瀏覽器看得懂的靜態檔,產出一個 dist/ 資料夾。這一步等同我在本機打 npm run build —— 所以我推之前如果先在本機跑一次 build,幾乎就能保證這裡不會紅燈。

      - name: Deploy to Cloudflare Pages
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          command: pages deploy dist --project-name=jiayuselfweb --commit-dirty=true

步驟 5:部署。 用 Cloudflare 官方的動作,把 dist/ 推到 Cloudflare Pages。網站在這一步更新完成。


密鑰(Secrets):為什麼不寫在檔案裡

注意上面的 ${{ secrets.CLOUDFLARE_API_TOKEN }}。那是一把能動我 Cloudflare 帳號的鑰匙,它絕對不能寫進程式碼(檔案會進 git 歷史、公開可見,等於把鑰匙貼在門上)。

正確做法:把鑰匙存在 GitHub repo 的 Settings → Secrets and variables → Actions 裡。存進去之後,流水線用 ${{ secrets.名字 }} 取用,但在 log 裡會被自動遮成 ***,誰都看不到原值。這個網站要兩把:CLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_ID

一句話原則:程式碼可以公開,鑰匙只能放 Secrets。


怎麼看一次失敗(紅燈)

自動化最怕「壞了不知道哪壞」。GitHub Actions 每一次跑都有紀錄:

  1. 到 repo 的 Actions 分頁,會看到一列一列的執行紀錄,綠勾 = 成功、紅叉 = 失敗。
  2. 點進紅叉那次,展開步驟,第一個變紅的步驟就是出事的地方。
  3. 展開它的 log,通常最後幾行的紅字就是原因。

最常見的三種紅燈:

  • build 失敗:多半是程式碼有錯,或某個檔案格式不對。對策 —— 推之前先在本機 npm run build 重現。
  • 裝套件失敗:package-lock.json 沒跟著更新。對策 —— 本機 npm install 後把 lock 檔一起 commit。
  • 部署失敗說沒權限:Secrets 沒設或設錯。對策 —— 回 repo Settings 檢查那兩把鑰匙。

自己從零設一條(可照做)

如果你也想要「push 就上線」,最小步驟:

  1. 在 repo 裡建資料夾與檔案:.github/workflows/deploy.yml(名字隨意,副檔名要 .yml,位置要對)。
  2. 貼上面那份設定檔,把 --project-name= 改成你自己的專案名、node-version 改成你的版本。
  3. 到託管平台(Cloudflare / Vercel / Netlify 都行)拿一把 API token
  4. 把 token 存進 GitHub repo 的 Secrets(名字要跟設定檔裡 ${{ secrets.XXX }} 對上)。
  5. git push。到 Actions 分頁看它跑。綠燈 = 成功上線。

之後你就再也不用手動上傳檔案了 —— 這就是我平常「改完 git push 就不管」的全部祕密。


誠實說:它的邊界

  • CI/CD 不會幫你檢查對錯。它只忠實地執行你寫的步驟;程式碼有邏輯錯誤但能建置成功,它照樣把錯的版本上線。要擋這種,得在流水線裡加測試步驟(這個網站目前沒有自動化測試,是刻意從簡)。
  • 紅燈會擋住上線,這其實是好事 —— 建置失敗時它不會把壞版本推出去,線上維持舊的可用版本。
  • 免費額度有上限:GitHub Actions 公開 repo 免費額度很大,但私有 repo 有每月分鐘數限制,量大要注意。

延伸閱讀