ChatGPT Web + GitHub + Self-hosted Runner:跨裝置開發,統一從 GitHub 部署
以前我做伺服器專案,開發和發布幾乎都繞不開同一台電腦:本機拉程式碼,用 Codex 或 IDE 修改,git push,接著開終端機 SSH 到伺服器,git pull、建置、重啟、看日誌。這種方式沒有什麼問題,甚至很直覺;真正麻煩的是專案開始長期維護之後,開發環境、本機工作目錄、SSH 工作階段和正式發布會一起綁在同一台機器上。
最近我把自己的部落格部署鏈路重新收斂,最後保留下來的正式流程是:
瀏覽器 / 本機開發 / 雲端 Coding Agent
↓
GitHub
↓
GitHub Actions
↓
Self-hosted Runner
↓
build / validate
↓
release directory
↓
current symlink
↓
production
我真正喜歡的不是「以後完全不用本機電腦」,而是開發入口可以改變,正式環境的出口盡量只保留一條。Mac 上寫程式、手機上看任務、瀏覽器裡繼續一個雲端 Coding 任務,都只是不同入口;真正需要長期穩定的是 GitHub 裡的程式碼狀態、審查紀錄和那條確定性的正式 workflow。
把筆記整理成文章時,我也重新對照了 GitHub、OpenAI 的目前文件,以及自己倉庫真正執行的 workflow。有兩個說法必須收緊:普通 ChatGPT 連上 GitHub,不等於自動擁有寫入儲存庫的權限;我現在這套部署也不能簡化成「任何失敗都會自動回滾」。
GitHub 目前對 Self-hosted Runner 的說明在這裡:https://docs.github.com/en/actions/reference/runners/self-hosted-runners
Self-hosted Runner 不是 GitHub 遠端 SSH 進你的伺服器
一開始很容易把它想成「GitHub 收到 push,再遠端登入 VPS 執行腳本」。實際模型不是這樣。
GitHub 官方文件寫得很清楚:Self-hosted Runner 應用程式跑在你自己的機器上,由 Runner 主動連線 GitHub 接收 job assignment;主機需要能向 GitHub 發起 outbound HTTPS 連線,核心通訊走 443 連接埠。
一台 VPS 註冊成 Runner 後,更像一個長時間在線的工作執行器:
GitHub
↑ Runner 主動建立連線
│
Self-hosted Runner on VPS
│
├─ checkout
├─ npm ci
├─ test / build
└─ local deploy
workflow 真正用來決定「誰來接這份工作」的是 runner label。公開文章裡,我把自己的標籤簡化成:
jobs:
deploy:
runs-on: [self-hosted, linux, prod]
這和舊的 SSH/SCP 發布方式差很多。以前 CI 在外部機器建置,之後還要把產物推進 VPS;現在 job 本身就在 VPS 的 Runner workspace 裡執行,建置完成後可以直接呼叫本機部署腳本,不需要再多維護一層 CI 到伺服器的 SSH key、SCP 路徑和遠端 shell 命令。
對我來說,GitHub 比較像控制平面和持久狀態中心,VPS 才是執行平面。只要受信任的 Runner 在線,究竟是哪個裝置發起程式碼修改就沒那麼重要,正式環境最終都會經過同一份 workflow。
這也讓我更願意把 GitHub 當成 source of truth。Runner workspace 不再是「伺服器上的主程式碼」,而只是某一次 job 的暫時執行目錄;真正需要追蹤的是 commit、PR、workflow run 和 release。
瀏覽器可以成為開發入口,但「連上 GitHub」不等於「可以寫 GitHub」
這一層最容易被產品宣傳語混在一起。
OpenAI 目前的 GitHub 連線文件,明確把普通 ChatGPT GitHub app 和具備寫入能力的 Coding workflow 分開。一般連線用途是讀取、搜尋和分析你授權的儲存庫;Help Center 也直接說明,若要生成、編輯並直接 push 程式碼到 GitHub,需要走 Codex 這類具備寫入權限的流程。
官方說明:https://help.openai.com/en/articles/11145903-connecting-github-to-chatgpt
所以我不會再把架構畫成:
ChatGPT Web
↓
直接修改 GitHub
比較準確的模型是:
瀏覽器 / 手機 / 桌面
↓
具備寫入權限的 Coding workflow
Codex Cloud / 受控 Agent
↓
branch / commit / PR
↓
GitHub
Codex Cloud 才真正讓「電腦不在身邊也能繼續開發」變得實用。OpenAI 目前文件說明,雲端任務會跑在 OpenAI 管理的電腦上,可以從支援的桌面、網頁或行動端繼續;本機電腦休眠後,雲端任務仍可以繼續執行。
對應說明:https://help.openai.com/en/articles/11369540-using-codex-with-your-chatgpt-plan
但我不會把這件事寫成「網頁版沒有 Token 限制」或「永遠沒有使用額度」。方案、工作區政策和產品限制都可能改變。真正穩定的優勢比較簡單:程式碼狀態不再依賴當下這一台 Mac 的終端機工作階段。
這也是我後來為什麼更願意把 Codex 看成一個開發入口,而不是一個模型視窗。真正讓工作可以換裝置繼續的,是儲存庫、環境、任務狀態和驗證鏈。
如果你想繼續看這個方向,我之前也寫過 新版 Codex 為什麼越來越像一個開發環境。
正式環境只剩 GitHub 一個出口後,我才開始真正相信「跨裝置開發」
只做到「瀏覽器裡能改程式碼」還不夠。如果最後發布仍然要「回 Mac、SSH 進伺服器再敲幾條命令」,那只是把寫程式這一段搬到雲端,正式環境的依賴仍然綁在本機。
我現在這套部落格 workflow 更重要的一點,是正式環境只接受受信任的 main 更新。
我重新檢查了目前真正執行的 workflow,大致順序是:
main push
↓
checkout
↓
runtime / directory checks
↓
npm ci
↓
syntax checks
↓
build
↓
capture previous release
↓
local atomic deploy
↓
post-deploy SEO verification
↓
submit sitemap
這不只是架構圖。2026 年 10 月 7 日,我核對了一次已經完成的正式 Actions run,Build、Atomic Deploy、Post-Deploy SEO Verification 和 sitemap submission 都成功結束。對我來說,這證明發布真的已經從「某台本機電腦上的 SSH 操作」搬進 repository workflow。
部署腳本本身也很簡單。公開示例可以抽象成:
/srv/app/
├─ releases/
│ ├─ 20261007-abc1234/
│ └─ 20261007-def5678/
└─ current -> releases/20261007-def5678/
新版本先進入獨立的 release 目錄,再用暫時 symlink 原子替換 current。Nginx 或其他 Web Server 一直只需要指向 current,不會直接吃某個瞬時 build workspace。
我也順手把自己原本的一句話修正得更精確。原先很容易把這套設計概括成「失敗就自動回滾」,但真正看 workflow 後,實際邊界是:
- build 或部署前檢查失敗時,根本不會切換正式版本;
- 部署後 SEO verification 失敗時,workflow 有明確邏輯把
current指回 previous release; - 這不代表所有發生在 symlink 切換之後的故障都有自動回滾路徑。
這個差別看起來有點吹毛求疵,但在運維上很重要。漂亮的架構圖很容易比實際程式碼更強。如果之後真的要擴大自動恢復能力,就應該把 failure class 一個個定義出來,而不是只靠「atomic deploy」幾個字產生安全感。
我之前那篇 Markdown + GitHub Actions 自動發布流程 解決的是比較小的問題:內容怎麼自動上線。這次這套架構解決的是另一層問題:不管開發從哪裡發起,哪一條路徑才有資格進正式環境。
Self-hosted Runner 最大的坑,是它真的會在你的機器上執行程式碼
把 Runner 放在 VPS 上很方便,正是因為 job 能碰到本機檔案系統、網路和部署目錄。風險也正好來自同一件事。
GitHub 的安全文件講得非常直接:Self-hosted Runner 不像 GitHub-hosted Runner 一樣,保證每個 job 都是全新、可丟棄的暫時虛擬機器。不受信任的 workflow code 可能持久污染主機。因此 GitHub 強烈不建議在 public repository 上使用 Self-hosted Runner,也提醒 private/internal repo 仍然要仔細看 fork/PR 路徑。
官方安全說明:https://docs.github.com/en/actions/reference/security/secure-use
看到這裡,我又重新檢查了一遍自己的 workflow。正式部署由受信任的 main push 觸發,這個方向沒問題;但目前內容 PR 的驗證任務也會跑在同一台 Self-hosted Runner。
在我現在這種私人儲存庫、受信任作者的使用模型裡,風險還是可控的。可是一旦未來把儲存庫公開,或讓更多外部協作者提交 PR,我不會再讓「PR 裡的任意程式碼」碰到正式 Runner。
我更願意拆成:
Untrusted / public PR
↓
GitHub-hosted runner
或獨立、可銷毀的 validation Runner
Protected main
↓
Production self-hosted runner
↓
Deploy
團隊再大一點時,Production Runner group 也應該只允許真正需要它的 repository 和 workflow 使用。
這也是為什麼「AI 不再需要 VPS SSH key」只算安全改善的一部分。拿掉 Agent 的直接 SSH 憑據很好,但如果 Agent 能修改 workflow,而 workflow 可以在正式主機上執行任意 shell,那權限只是換了一個入口,並沒有消失。
真正要保護的是整個執行邊界:誰可以修改 workflow、誰可以 merge 到 main、哪些事件能命中 Production Runner、Runner 使用者可以寫哪些目錄、Secrets 到底有多大權限。
本機 Codex 和 SSH 沒有消失,只是退回更適合的位置
做到這裡之後,我反而不覺得本機 Codex、本機儲存庫和 SSH 變落後了。
它們處理的是瀏覽器控制平面最不擅長的東西:即時的機器狀態。
我現在會這樣分:
| 情境 | 雲端 / GitHub workflow | 本機 Codex + SSH |
|---|---|---|
| 跨裝置繼續 Coding 任務 | 很適合 | 依賴目前這台機器 |
| 程式碼歷史與 PR 審查 | 很適合 | 仍以 GitHub 為準 |
| 標準 build / test / deploy | 很適合 | 不該每次手動做 |
查看 docker ps / journalctl | 間接 | 很適合 |
| 資料庫、磁碟、網路現場排查 | 間接 | 很適合 |
| 正式環境緊急處理 | 可協助 | SSH 更直接 |
| 可稽核發布 | 天生有 run / commit | 手動 SSH 較弱 |
瀏覽器或手機很適合當 control plane:決定要改什麼、看 diff、審查 CI、觸發已經定義好的 workflow。本機終端機更像 observability/debug plane:現在到底是哪個 process 卡住、哪個 container 一直重啟、磁碟為什麼滿、Nginx 實際載入的是哪份設定。
SSH 也不需要完全移除。我更願意把它降級成 break-glass 通道:日常發布不依賴它,真正遇到正式故障時仍然可以直接進機器。臨時在伺服器上做的修復,最後也應該回到 GitHub 形成 commit 或 workflow 狀態,而不是讓機器長期累積只有某台電腦知道的現場修改。
如果你已經有一套本機 Markdown + GitHub Actions 流程,可以先看 我之前的自動發布方案;如果還在比較靜態網站和傳統 CMS,那篇 Astro vs WordPress 比較接近架構選擇這一層。
我現在更在意的是這四個角色終於可以分開:
瀏覽器 / Codex / 本機 IDE → 開發入口
GitHub → 持久程式碼與審查狀態
Self-hosted Runner → 受信任的自動執行
SSH → 深度診斷與緊急通道
裝置可以換,Agent 可以換,甚至正式伺服器以後也可以換。只要程式碼狀態、部署規則和驗證邏輯都留在 GitHub,整條鏈路就不會跟著某一台電腦一起消失。
這才是我覺得 ChatGPT/Codex + GitHub + Self-hosted Runner 最有價值的地方:不是把終端機藏起來,而是盡量把「這件事一定要在這台電腦上親手完成」從日常流程裡拿掉。