Skip to content

ci: keep the station copies in the commit that changes them - #294

Merged
whes1015 merged 1 commit into
mainfrom
ci/station-copies-in-pr
Oct 11, 2026
Merged

whes1015 merged 1 commit into
mainfrom
ci/station-copies-in-pr

Conversation

@whes1015

@whes1015 whes1015 commented Oct 11, 2026 •

Copy link
Copy Markdown
Member

這個 PR 做了什麼

讓 bot 把 station.csv / station.json 這兩個副本直接改進 PR 自己的 commit,main 上不再出現 bot 補的 chore: update station.csv from station-dev.csv [skip ci]。rebase 合併之後,每次修改就是一個 commit,跟 DPIP、TREM-Lite 一樣乾淨。

目前前 300 個 commit 裡,有 95 個是 bot 補的副本 commit,每個 fix: station 後面都跟著一個。

流程

情況 以前 現在
同 repo 的 PR 改了 station-dev.* PR 只驗證;合併後 bot 直接推一個 commit 到 main bot 在 PR 的每個 commit 重跑產生器,副本有變就 amend 進那個 commit(作者不變,committer 是 github-actions[bot]),再用 --force-with-lease 推回 PR 分支
fork 的 PR 同上 拿不到 secret、推不回去,維持以前的做法:合併後由 bot 補
PR 手改了副本 不會擋(protect 的失敗不是必過 check);合併後 protect 用作者名稱、email 判斷,不是 bot 就 force push 一個 revert 到 main。任何 GitHub noreply email 都會被當成 bot;commit 訊息含 skip ci 字串時 workflow 根本不會跑 合併前就擋下:PR 改了副本,但內容跟產生器輸出不同,station-*-check 失敗
有人直接 push 到 main 改副本(只有能略過規則的人做得到) 同上,事後 revert main 上重新產生,用一般 commit 蓋回正確結果,不用 force push
必要檢查 station-csv-check、station-json-check 名稱不變;-dev 檔和副本都沒動到就直接通過,跟以前一樣

幾個設計上的重點:

  • 推回 PR 分支要用 PAT_TOKEN。 用 GITHUB_TOKEN 推上去的 commit 不會觸發新的 run(官方文件),必要檢查會一直等不到結果。
  • lease 釘在這次 run 看到的 head。 作者在 bot 改寫期間又推了新的東西,bot 就不會蓋掉,交給那次 push 觸發的 run 處理。
  • 分支裡有 merge commit 就不改寫。 rebase 會把 merge 攤平,改到的就不只副本,這種情況改成合併後由 bot 補。
  • 中間某個 commit 驗證不過也沒關係。 產生器在驗證失敗時不會寫檔,那個 commit 維持原樣,錯誤交給必要檢查回報。
  • 四個 workflow 合成一個 station.yml。 以前 csv 和 json 兩個 job 都可能同時推 main,後推的那個會被拒絕;現在在同一個 job 裡依序處理。

副本的保護

副本仍然只能是產生器的輸出,而且現在是合併前就擋下,不是合併後才 revert。判斷規則是看 PR 合併後的副本:

  • 跟 base 一樣(PR 沒碰副本):放行。如果 dev 有改,副本只是還沒更新,合併後由 bot 補。這是 fork 的情況。
  • 跟產生器輸出一樣(bot 改寫過):放行。
  • 都不一樣:失敗,訊息指向 resource/station.csv。

改變的是 git 紀錄。副本現在跟著貢獻者的 commit 進 main,所以 git log -- resource/station.csv 看到的 author 是貢獻者,committer 才是 github-actions[bot]。內容仍然百分之百由產生器產生。要讓 main 歷史乾淨,這一點沒辦法同時保留。

還剩兩個管道能影響副本:

  • 在 PR 裡改產生器或 workflow 本身。 這要靠 code review 擋,跟以前一樣。這個 repo 沒有 CODEOWNERS,所以 ruleset 的「需要 code owner 審查」目前沒有作用。
  • 能略過規則的人直接 push 到 main,訊息又帶 skip ci 字串。 這樣 workflow 不會跑,副本不會被蓋回。目前只有 whes1015 做得到。

對貢獻者的影響

bot 改寫之後,PR 分支的 commit SHA 會變。用 GitHub 網頁編輯的不受影響;在本機繼續改的話,要先同步遠端:

git fetch origin
git reset --hard origin/<分支名>

怎麼驗

在 scratchpad 用這個 repo 的副本,模擬 bot 的 rebase(腳本是直接從 station.yml 抽出來的那一段):

  • PR 有兩個 commit,一個無關、一個改了 station-dev.csv:無關那個 SHA 不變;改了 dev 的那個併入 station.csv,作者仍是原作者。再跑一次,HEAD 不變,不會無限改寫。
  • 中間 commit 的 lat 超出範圍、下一個 commit 修好:rebase 正常完成,副本只併進修好的那個 commit。
  • 在沒有 git user 設定的環境下(跟 runner 一樣)也能完成 amend。
  • 本機在 main 上跑兩支產生器,輸出跟目前的副本完全相同,所以合併後 main 上的兜底 job 不會產生多餘的 commit。
  • actionlint 1.7.12 沒有警告。

真正的「推回 PR 分支、PAT 觸發新 run」要在 GitHub 上才驗得到,我會另開一個草稿 PR 實測,結果補在下面的留言。

@whes1015

Copy link
Copy Markdown
Member Author

在草稿 PR #295(疊在這個分支上,只改了 station-dev.csv 一行)實測過,已關閉並刪除分支:

run commit sync 必過 check
38155073788 db69272(原本的 commit) 把 station.csv 改進 commit,用 PAT 推回分支 略過(這個 commit 已經不是 head)
38155087560 d54147c(bot 改寫後) 副本已是最新,不推 station-csv-check、station-json-check 都通過
  • 改寫後的 d54147c 同時包含 station-dev.csv 和 station.csv,author 還是原作者,committer 是 github-actions[bot]。
  • 第二個 run 是 PAT 推送觸發的,代表必過 check 會在新的 head 上自動重跑,不會卡在 Pending。

另外修正了一個問題:這個 PR 原本的 commit 訊息內文引用了 bot 的 commit 標題,裡面含有 [skip ci]。GitHub 看到 PR head commit 含有這個字串,就會跳過 pull_request workflow,必過 check 會一直停在 Pending(官方文件)。已經改掉訊息裡的字面字串並 force-push,內容沒變。現在 station-csv-check、station-json-check 都通過了。

station.csv and station.json are generated from their -dev files. The
bot did that after a merge, pushing a "chore: update station.csv from
station-dev.csv" commit straight to main with CI skipped, so every
station fix landed as two commits: 95 of the last 300 on main were the
bot's.

Do it inside the pull request instead. When a same-repository pull
request opens or moves, the bot reruns both generators in each of its
commits, amends any copy that changed into that commit (author kept,
committer github-actions[bot]) and pushes the branch back with a lease
on the head it saw. The push goes out with PAT_TOKEN, because commits
pushed with GITHUB_TOKEN start no workflow run and the required checks
would never report. A rebase merge then lands one commit per change.

A copy is still never written by hand, and that is now enforced before
a merge instead of reverted after one. station-csv-check and
station-json-check fail any pull request whose copy differs from what
the generator makes of its -dev file. A pull request that leaves the
copy alone passes even if it is stale: a fork gets no secrets, so the
bot cannot push to it, and its copies are generated on main after the
merge as before. On main, a copy that does not match is regenerated
and pushed, which covers a direct push by a ruleset bypass actor.

That replaces protect-station-*.yml, which let a hand edit merge and
then force-pushed a revert to main, judging commits by author name and
email: any GitHub noreply address passed, and a push with CI skipped in
its message never ran it at all.

All four workflows become one, so the two jobs cannot race to push to
main. station-csv-check and station-json-check keep their names, which
the ruleset requires, and pass a pull request that does not touch the
-dev file or the copy, as before.
@whes1015

Copy link
Copy Markdown
Member Author

補強副本的保護(已 force-push,commit 訊息同步更新):原本 PR 手改副本時檢查只發 warning,在 bot 改寫不了的情況下(fork、分支裡有 merge commit),手改的副本可以合併進 main。現在 station-*-check 只要發現 PR 改了副本、而內容跟產生器輸出不同就會失敗。

在草稿 PR #296 實測,已關閉並刪除分支:

步驟 run 結果
只手改 station.csv 一行 38156888133 sync 放棄改寫(蓋回後會變成空 commit);station-csv-check 失敗,錯誤標在 resource/station.csv
換成只改 station-dev.csv 38156958226 bot 把副本改進 commit 並推回(5570092,committer github-actions[bot]);兩個必過 check 都通過

本機另外用從 workflow 抽出來的腳本測過:沒動到 station 檔、只改 dev(fork 的情況)、dev 加上產生的副本、dev 加上產生後又手改、dev 不合法,五種結果都符合預期。

@whes1015
whes1015 merged commit 6890c2c into main Oct 11, 2026
7 checks passed
@whes1015
whes1015 deleted the ci/station-copies-in-pr branch October 11, 2026 16:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant