Skip to content

ci: add CD pipeline to GHCR with hardened Dockerfile#55

Merged
torrid-fish merged 5 commits into
mainfrom
ci/cd-ghcr
May 30, 2026
Merged

ci: add CD pipeline to GHCR with hardened Dockerfile#55
torrid-fish merged 5 commits into
mainfrom
ci/cd-ghcr

Conversation

@torrid-fish

@torrid-fish torrid-fish commented May 28, 2026

Copy link
Copy Markdown
Member

目的

把原本 PR #53 (feat/local-unidic) 中與 UniDic 遷移無關、屬於通用 CI/CD 基建的 commit 拆出來成獨立 PR,先合進 main,讓 main 能跑出可運行的 production image,#53 的 diff 也能瘦身到只剩 accent pipeline 改動。

strict stacked PR:本 PR 合併前,PR #53 的 base 會暫時切到 ci/cd-ghcr;本 PR 進 main 後,#53 的 base 會切回 main,diff 自動清掉這 3 commit 的內容。

#53 仍會保留一個 UniDic-specific 的 Dockerfile patch(unidic download at build),因為它依賴 #53 才加入的 unidic pip dep,無法獨立到本 PR。

範圍(3 commits, base main

  1. ci: add CD workflow to build + push image to GHCR.github/workflows/cd.yml
    • Triggers on push to main + v*.*.* tags + manual workflow_dispatch
    • 非預設分支只 tag <branch-name> + short SHA;dev 鎖在 default branch,stable 鎖在 version tag — 任何非 main push 不會動到 production tag
    • Image name: ghcr.io/sessatakuma/api-tools(lowercase hardcoded — github.repository 為混合大小寫 sessatakuma/API-tools,GHCR 要全小寫)
    • GHA cache 啟用(type=gha, mode=max
  2. ci: bump CD actions to Node 24 majorsactions/checkout@v6docker/*-action@v4..v7
    • GitHub runners 將於 2026-06-02 強制下架 Node 20,先升級避免到期當天工作流壞掉
  3. fix(docker): run container as non-root user (least privilege)Dockerfile
    • 建立 appuser (uid 10001) 並 USER appuser,App 綁 8000 (>1024) 所以不需要 root;app 檔案保持 root-owned、對 process read-only

驗證

Out of scope

🤖 Generated with Claude Code

torrid-fish and others added 3 commits May 28, 2026 21:32
Triggers on push to main and v*.*.* tags, plus manual workflow_dispatch.
Non-default branch builds tag the image by ref + short SHA; `latest` is
gated to the default branch and `stable` to version tags, so non-main
builds never clobber production tags.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
GitHub is forcing Node 20 actions off the runners from 2026-06-02.
Bump checkout v4->v6 and the docker/* actions to their latest majors,
all of which run on Node 24. Inputs are unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
No USER directive meant uvicorn ran as root (uid 0). Add an
unprivileged `appuser` (uid 10001) and switch to it before CMD. The
app binds 8000 (>1024) so root isn't needed; app files stay root-owned
and read-only to appuser. Verified the image runs as uid 10001 and
/api/MarkAccent/ still returns 200.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@wade00754 wade00754 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 [blocking] 檔案結尾缺少換行符

.github/workflows/cd.yml 第 68 行末尾缺少換行符(\ No newline at end of file)。YAML 檔案應以換行結尾,否則可能被 yamllint 或部分工具標記。請在檔案最後加上一個換行。


🟡 [important] 缺少 PR 階段的 Docker build CI

CD workflow 只在 push 到 main(合併後)才觸發,但目前的 PR CI(main.yml)只有程式碼品質檢查,不包含 Docker build 驗證。若 Dockerfile 有語法錯誤或 build 失敗,會在合併後才被發現。

建議在 main.yml 或獨立 CI workflow 中加入 Docker build-only 步驟:

- name: Verify Docker build
  run: docker build -t test-image .

🟡 [important] latest 標籤在並行 CD 執行時可能有競態問題

concurrency 設了 cancel-in-progress: false,若短時間內有多個 push 到 main,多個 CD 執行會同時進行。此時 latest 標籤會被最後完成的(而非最晚 push 的)覆蓋,可能導致 latest 指向較舊的 commit。

兩個方案:

  • 方案 A:改為 cancel-in-progress: true,取消舊的 CD(但可能中斷進行中的部署)
  • 方案 B:保持不變,但確保部署目標使用 shasemver 標籤拉取 image,latest 僅作為 convenience tag

如果部署目標已經用 shasemver tag 拉取,這個問題影響不大,可以降級為 [suggestion]


💡 [suggestion] 考慮加入 provenancesbom

本 PR 強調 hardened Dockerfile,docker/build-push-action@v7 支援 provenancesbom 屬性:

  • Provenance(SLSA 來源證明):加密簽名的 metadata,記錄 image 由哪個 commit、在哪個 CI runner 上建構的,讓消費者能驗證 image 未被篡改
  • SBOM(軟體物料清單):列出 image 內所有套件的版本與依賴關係,方便漏洞掃描與合規審計

設定方式:

provenance: true
sbom: true

目前專案規模尚小,不急著加,但既然主打 hardened 是個好時機順帶處理。

@torrid-fish

Copy link
Copy Markdown
Member Author

檔案結尾缺少換行符

已修正

缺少 PR 階段的 Docker build CI

已測試 過此CI可以正常執行

latest 標籤在並行 CD 執行時可能有競態問題

已修改成 cancel-in-progress: true,如果之後發現有遇到短時間內多個CD在跑壓過彼此則再手動重啟即可。

考慮加入 provenancesbom

已加入

@torrid-fish
torrid-fish requested a review from wade00754 May 30, 2026 10:28
@github-actions

Copy link
Copy Markdown

🛡️ PR Quality Check Summary

PR Title: Passed (Length: 52/75, Format: OK). ci: add CD pipeline to GHCR with hardened Dockerfile
Branch Name: Follows naming convention (ci/cd-ghcr)
Commit Messages: All 5 commit(s) passed (Length, Format, Case)
Conflicts: No merge conflict markers found
Python Quality: All checks passed.


🎉 All checks passed!

@wade00754 wade00754 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感謝快速修正!但關於 PR 階段 Docker build CI 這項,我的意思是現在這個 CD 只有在 push 到 main 的時候才會觸發,如果 Docker build 失敗的話 main 上的 image 會停留在舊版本,直到有人手動修復再重新 push。如果在 PR CI 中加一個 docker build 步驟(只 build 不 push),就能在合併前攔截這類問題。

可以開 issue 在未來解決

@torrid-fish
torrid-fish added this pull request to the merge queue May 30, 2026
Merged via the queue into main with commit 0c17733 May 30, 2026
6 checks passed
@torrid-fish
torrid-fish deleted the ci/cd-ghcr branch May 30, 2026 11:40
@torrid-fish

Copy link
Copy Markdown
Member Author

喔喔你的意思是這樣。
但這個確實是CI的問題,應該是在PR開起來的時候要測試的項目。同意可以加個 action 和 PR Quality Check 之後檢查之類的。

@torrid-fish torrid-fish self-assigned this May 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants