diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9dd7dfb..24219cc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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] @@ -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 diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 734ec1a..4a951f2 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -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 @@ -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