Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 35 additions & 27 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,17 @@ name: CI
on:
push:
branches: [main]
# 開了 PR 就在合併前先驗一次,但**草稿不跑**(下面每個 job 的 `if`)。
# 迭代期間開成 draft,改好再標 ready for review——那一刻才觸發。
# 開了 PR 就在合併前先驗一次,**草稿也跑**(裁示 2026-08-21)。
#
# 這與「工作分支的頻繁推送不該花 CI」並不衝突:沒有開 PR 的分支不會觸發,
# 開了 PR 但還是草稿的也不會。換到的是**合併前就知道壞了**,
# 而不是像 2026-08-08 那次要等合併後才發現 hoshi-deploy 是紅的。
# 沒有開 PR 的分支仍然不會觸發,所以「工作分支的頻繁推送不該花 CI」照舊
# 成立;改掉的是草稿那一段。草稿不跑換到的是迭代期間省下的分鐘數,而升上
# Enterprise 之後那不是收益;付出的是**最需要早點知道的那個階段沒有網子**。
#
# > **註**:這段註解在 2026-08-21 之前寫著「草稿不跑(下面每個 job 的
# > `if`)」,而那個 `if` **十五個倉庫裡只有兩個真的有**(hoshi-build 與
# > hoshi-standards)。也就是說十三個倉庫的草稿 PR 一直都在跑 CI,
# > 註解描述的是一件沒有發生的事。它的方向是「以為更省」,
# > 所以不會有任何帳單或紅燈讓人發現它——**一份說謊的註解不會自己壞掉**。
pull_request:
branches: [main]
types: [opened, synchronize, reopened, ready_for_review]
Expand Down Expand Up @@ -54,34 +59,37 @@ jobs:

- run: npm run build

# **本倉庫是公開的,所以掃描與 `build` 同時起跑**,不等它綠燈。
#
# 公開倉庫的 Actions 分鐘數不計費,所以「基礎驗證紅燈時整組 skipped、
# 省下執行器時間」在這裡換不到任何東西,只換到更晚才知道掃描的結論。
# 代價要講清楚:建置壞掉時 CodeQL 的 autobuild 會吐同一個錯誤換一種包裝,
# 所以一顆真的紅燈會伴隨一顆指向同一個根因的紅燈。
# 安全掃描接在基礎驗證之後:基礎驗證紅燈時整組 **skipped**,而不是拿一份
# 編譯不過的原始碼去跑掃描,再收到一顆與根因無關的第二紅燈。
#
# **私有倉庫維持 `needs:` 那道閘**——那裡的分鐘數要付錢。兩種形狀是刻意的,
# 不是漂移;判準與理由見 workspace `AGENTS.md` §1.9.1。
# > **註**:這段文字到 2026-08-21 為止寫的是「**本倉庫是公開的,所以掃描與
# > 基礎驗證同時起跑**,不等它綠燈」,理由是公開倉庫的 Actions 分鐘數不計費。
# > 那個形狀在 2026-08-16 就被取代了,而**取代它的 `needs:` 一直都在**
# > (就在下面幾行,還帶著自己的註解)——沒有跟上的只有這段文字。
# >
# > 它還聲明「兩種形狀是刻意的,不是漂移;判準見 workspace `AGENTS.md`
# > §1.9.1」,而 §1.9.1 說的正好相反。**一段指著條文說自己不必照辦的註解,
# > 比沒有註解更糟**:它讓讀者以為這裡已經對照過了,於是沒有人去比對。
#
# 掃描本身的定義在 `security.yml`,那支同時還掛著每週排程。
# 掃描本身的定義在 `security.yml`,那支同時還掛著每週排程(**不**經過這道閘)
security:
# **這一組在 PR 上也跑,但裡面只有 Gitleaks 會動**(裁示 2026-08-16)。
#
# 判準是「漏到 `main` 之後要付什麼」:
# **這一組在 PR 上全部會跑**(裁示 2026-08-21)。
#
# | | 補救 |
# |---|---|
# | CodeQL/govulncheck/Trivy | **修程式**,修好就結束 |
# | **Gitleaks** | 刪掉不夠——憑證已經在 git 歷史裡,**必須輪替** |
# 2026-08-16 到 08-21 之間只有 Gitleaks 會動,其餘三種各自帶一條
# `if: github.event_name != 'pull_request'`。當時的判準是「漏到 `main`
# 之後要付什麼」:CodeQL/govulncheck/Trivy 找到的東西**修一次就結束**,
# 在 PR 攔下與在 `main` 攔下代價一樣,所以省下每個 PR 數分鐘;只有
# Gitleaks 代價不同(憑證已經在 git 歷史裡,刪掉不夠,**必須輪替**),
# 所以它留在 PR。
#
# 前三種在 PR 攔下與在 `main` 攔下代價一樣,所以留在 `main`(省下每個 PR
# 數分鐘);Gitleaks 是唯一代價不同的,所以掛回 PR。
# **前半仍然成立,後半不再是收益**:升上 Enterprise 之後分鐘數不是稀缺的
# 東西了,而代價一直都在——一個在 `main` 才被攔下的問題,是一個**已經
# 合併**的問題。判準因此收斂成一句:**CI 做得到的驗證,就讓它在合併前跑。**
# 那三條 `if` 已於 2026-08-21 從 `security.yml` 拆掉。
#
# **閘門下移到被呼叫的那三個 job 上**(`security.yml` 裡各自的
# `if: github.event_name != 'pull_request'`),不是留在這裡——留在這裡會把
# Gitleaks 一起關掉。這樣做同時保住了檢查名稱:它仍然是
# `安全掃描 / Gitleaks`,按名字釘住的分支保護不受影響。
# **`needs: [verify]` 那道閘留著,理由與成本無關**:建置壞掉時 CodeQL 的
# autobuild 與 govulncheck 吐的是同一個編譯錯誤換一種包裝,多餘的紅燈會
# 稀釋掉真的那一顆。每週排程**不**經過它(見上方 `on:` 的註解)。
name: 安全掃描
# 閘門對公開倉庫也成立了(裁示 2026-08-16):掃描要先等基礎驗證綠。
# 取代 workspace decisions/infrastructure/公開倉庫的掃描不經閘門.md
Expand Down
36 changes: 20 additions & 16 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,18 @@ permissions:

jobs:
codeql:
# 這一種在 PR 上不跑(裁示 2026-08-16):找到的東西是「修一次就結束」的,
# 在 PR 攔下與在 `main` 攔下代價一樣。Gitleaks 沒有這一行——它是例外,
# 理由見 `ci.yml` 的 `security` job。
# **在 PR 上也跑**(裁示 2026-08-21)。
#
# 用 `!= 'pull_request'` 而不是 `== 'push'`:後者會順手把排程與
# `workflow_dispatch` 的手動掃描也關掉,而那兩個都是要用的。
# 這裡從 2026-08-16 起有一條 `if: github.event_name != 'pull_request'`,
# 理由是「找到的東西是修一次就結束的,在 PR 攔下與在 `main` 攔下代價一樣,
# 所以省下每個 PR 數分鐘」。**那句話的前半仍然成立,後半不再是收益**:
# 升上 Enterprise 之後分鐘數不是稀缺的東西了。
#
# 被 `ci.yml` 呼叫時,這裡讀到的 `github.event_name` 是**呼叫端的**事件,
# 所以這個判斷成立。
if: github.event_name != 'pull_request'
# 而代價一直都在:一個在 `main` 才被攔下的問題,是一個**已經合併**的問題。
# 判準因此收斂成一句——**CI 做得到的驗證,就讓它在合併前跑。**
#
# 每週排程仍然留著,它回答的是另一個問題:**沒有動過的程式**今天有沒有
# 變成有問題的(見上方 `on:` 的註解)。
name: CodeQL (${{ matrix.language }})
runs-on: ubuntu-latest
timeout-minutes: 40
Expand Down Expand Up @@ -186,16 +188,18 @@ jobs:
if-no-files-found: warn

trivy:
# 這一種在 PR 上不跑(裁示 2026-08-16):找到的東西是「修一次就結束」的,
# 在 PR 攔下與在 `main` 攔下代價一樣。Gitleaks 沒有這一行——它是例外,
# 理由見 `ci.yml` 的 `security` job。
# **在 PR 上也跑**(裁示 2026-08-21)。
#
# 這裡從 2026-08-16 起有一條 `if: github.event_name != 'pull_request'`,
# 理由是「找到的東西是修一次就結束的,在 PR 攔下與在 `main` 攔下代價一樣,
# 所以省下每個 PR 數分鐘」。**那句話的前半仍然成立,後半不再是收益**:
# 升上 Enterprise 之後分鐘數不是稀缺的東西了。
#
# 用 `!= 'pull_request'` 而不是 `== 'push'`:後者會順手把排程與
# `workflow_dispatch` 的手動掃描也關掉,而那兩個都是要用的。
# 而代價一直都在:一個在 `main` 才被攔下的問題,是一個**已經合併**的問題。
# 判準因此收斂成一句——**CI 做得到的驗證,就讓它在合併前跑。**
#
# 被 `ci.yml` 呼叫時,這裡讀到的 `github.event_name` 是**呼叫端的**事件,
# 所以這個判斷成立。
if: github.event_name != 'pull_request'
# 每週排程仍然留著,它回答的是另一個問題:**沒有動過的程式**今天有沒有
# 變成有問題的(見上方 `on:` 的註解)。
name: Trivy
runs-on: ubuntu-latest
timeout-minutes: 25
Expand Down