ci: add CD pipeline to GHCR with hardened Dockerfile#55
Conversation
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>
There was a problem hiding this comment.
🔴 [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:保持不變,但確保部署目標使用
sha或semver標籤拉取 image,latest僅作為 convenience tag
如果部署目標已經用 sha 或 semver tag 拉取,這個問題影響不大,可以降級為 [suggestion]。
💡 [suggestion] 考慮加入 provenance 和 sbom
本 PR 強調 hardened Dockerfile,docker/build-push-action@v7 支援 provenance 和 sbom 屬性:
- Provenance(SLSA 來源證明):加密簽名的 metadata,記錄 image 由哪個 commit、在哪個 CI runner 上建構的,讓消費者能驗證 image 未被篡改
- SBOM(軟體物料清單):列出 image 內所有套件的版本與依賴關係,方便漏洞掃描與合規審計
設定方式:
provenance: true
sbom: true目前專案規模尚小,不急著加,但既然主打 hardened 是個好時機順帶處理。
檔案結尾缺少換行符已修正 缺少 PR 階段的 Docker build CI已測試 過此CI可以正常執行
|
🛡️ PR Quality Check Summary✅ PR Title: Passed (Length: 52/75, Format: OK). 🎉 All checks passed! |
wade00754
left a comment
There was a problem hiding this comment.
感謝快速修正!但關於 PR 階段 Docker build CI 這項,我的意思是現在這個 CD 只有在 push 到 main 的時候才會觸發,如果 Docker build 失敗的話 main 上的 image 會停留在舊版本,直到有人手動修復再重新 push。如果在 PR CI 中加一個 docker build 步驟(只 build 不 push),就能在合併前攔截這類問題。
可以開 issue 在未來解決
|
喔喔你的意思是這樣。 |
目的
把原本 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 的內容。範圍(3 commits, base
main)ci: add CD workflow to build + push image to GHCR—.github/workflows/cd.ymlmain+v*.*.*tags + manualworkflow_dispatch<branch-name>+ short SHA;dev鎖在 default branch,stable鎖在 version tag — 任何非mainpush 不會動到 production tagghcr.io/sessatakuma/api-tools(lowercase hardcoded —github.repository為混合大小寫sessatakuma/API-tools,GHCR 要全小寫)type=gha, mode=max)ci: bump CD actions to Node 24 majors—actions/checkout@v6、docker/*-action@v4..v7fix(docker): run container as non-root user (least privilege)—Dockerfileappuser(uid 10001) 並USER appuser,App 綁 8000 (>1024) 所以不需要 root;app 檔案保持 root-owned、對 process read-only驗證
docker build在本分支成功(main 沒有unidicdep,不會嘗試unidic download— 這正是 feat(accent): local UniDic + POS-driven patches #53 才需要的 step)docker run確認 process 以uid=10001(appuser)啟動(非 root)YAHOO_API_KEYenv var — 這是 main 的既有行為(feat(docker): add docker compose setup for api-tools #46 docker compose 設定就已要求),本 PR 不改動 runtime config;feat(accent): local UniDic + POS-driven patches #53 合進來時 Yahoo MA 整段會被移除Out of scope
🤖 Generated with Claude Code