D4:管理企業的 GitHub Actions
D4-O1 散布與治理 actions 與工作流程
治理要回答兩件事:別人能用哪些 actions 與工作流程,以及它們跑起來有多大權限。可重用的元件本身(可重用工作流程、複合 action、組織起始工作流程)見領域二與三;本節講政策。
組織層級:哪些 actions 可以執行
在組織的 Settings → Actions → General → Policies 選擇:
- 對所有 repository 啟用 Actions。
- 完全停用 Actions。
- 啟用,但限制可執行的 actions 與可重用工作流程(選項名稱是「Allow OWNER, and select non-OWNER, actions and reusable workflows」)。
選「只允許組織自己的 actions」會封鎖所有 GitHub 作者的 actions,所以 actions/checkout 也不能用。要放行其他來源時,可以選:
| 選項 | 允許什麼 |
|---|---|
| GitHub 建立的 actions | actions 與 github 組織裡的 actions |
| Marketplace 上的已驗證建立者 | 已驗證建立者發佈在 Marketplace 的 actions |
| 指定的 actions 與可重用工作流程 | 依樣式放行或封鎖 |
樣式語法:action 用 OWNER/REPOSITORY@TAG-OR-SHA,可重用工作流程用 OWNER/REPOSITORY/PATH/FILENAME@TAG-OR-SHA;* 是萬用字元,, 分隔多個樣式;前面加 ! 表示封鎖。預設只有列出的樣式被允許,但 *, !space-org/action@* 表示「除了這個 action 之外全部允許」。指定的樣式不能超過 1000 個。如果工作流程受相依性鎖定檔(lockfile)涵蓋,以標籤或分支寫的項目比對不到,要改用萬用字元或完整的 commit SHA。
要求固定到完整 commit SHA:啟用「Require actions to be pinned to a full-length commit SHA」後,對組織的 actions 與 GitHub 作者的 actions 生效;可重用工作流程仍然可以用標籤參照;已被鎖定檔鎖定的相依項目也滿足這個要求,不必修改工作流程。
企業層級:政策的覆蓋方式
- 企業沒有強制政策時,組織擁有者與有「Manage organization Actions policies」權限的人可以完全控制自己組織的 Actions。
- 企業啟用某政策時,組織或 repository 還可以選擇性地停用它;企業停用某政策時,個別組織或 repository 不能再啟用它。
- 組織對 Actions 的存取可設成:所有組織啟用、特定組織啟用、所有組織停用(停用也會擋掉受影響組織的 code scanning 與 Code Quality 分析)。
- 允許的 actions:全部允許、只允許企業的、企業加上選定的非企業 actions(選項與組織層級相同)。「只允許企業的」同樣會擋掉
actions/checkout這類 GitHub 作者的 actions。政策不會限制本機 action(./路徑)。 - 企業的快取上限是最大值:組織與 repository 可以設更低,不能更高。
來自 fork 的 pull request
公開 repository 的外部協作者,可在組織層級選擇哪些人的 fork PR 工作流程需要核准:
- 剛加入 GitHub 的首次貢獻者;
- 首次貢獻者;
- 所有外部協作者。
預設是所有首次貢獻者都需要核准。由 pull_request_target 觸發的工作流程一律執行,不受這些設定影響。組織層級的變更會覆寫企業層級的設定。已有任何變更被合併的使用者,在前兩個選項下不再算首次貢獻者。
私有與 internal repository 的 fork PR 則有四種政策:執行工作流程(唯讀 GITHUB_TOKEN、預設沒有 secrets)、傳送寫入權杖、傳送 secrets、要求核准。
預設的 GITHUB_TOKEN 權限
- 新組織預設,
GITHUB_TOKEN對contents與packages範圍只有唯讀。可以選寬鬆(所有範圍都可讀寫)或限制式的預設。 - 如果企業選了更嚴格的預設,組織就不能再選更寬鬆的;組織的限制式預設會套用到它的 repository,並讓 repository 的寬鬆選項無法使用。
- 官方也寫到:在 2023 年 2 月 2 日當天或之後建立的企業或組織,預設對所有範圍唯讀;更早的預設是讀寫。
- 工作流程預設不能建立或核准 pull request,由「Allow GitHub Actions to create and approve pull requests」控制,預設停用。
保留期、快取與自訂映像的上限
| 項目 | 預設 | 可設定範圍 |
|---|---|---|
| 檢查、工作流程執行、狀態、成品、日誌 | 90 天 | 公開 repository 1–90 天;私有與 internal 1–400 天 |
| 快取保留 | 7 天 | 企業最大:公開 90 天、私有與 internal 365 天 |
| 每個 repository 的快取容量 | 10 GB | 企業最大可到 10,000 GB;超過方案內含的 10 GB 要額外計費 |
| 每個自訂映像的版本數上限 | 20 | 1–100 |
| 未使用版本的保留 | 30 天 | 1–90 |
| 版本的最長存在時間 | 60 天 | 7–90 |
官方還提到:從 2026 年 10 月 1 日起,這些保留政策也套用到檢查、工作流程執行與 commit 狀態。
這次沒有核對到:組織層級的「required workflows」(規則集的「要求工作流程通過」)。我讀了規則集的可用規則頁面,沒有找到它,備考前請在官方頁面確認。
判斷練習
情境 A: 組織想只允許自家的 actions 與 actions/*、github/*,其餘一律禁止。
解析: 選「允許組織自己的與選定的非組織 actions」,勾選「GitHub 建立的 actions」;不要選「只允許組織自己的」,那會把 actions/checkout 也擋掉。
情境 B: 組織要求所有第三方 actions 都固定到完整 SHA,但團隊的可重用工作流程仍用 @v2 標籤。
解析: SHA 固定要求不適用於可重用工作流程,標籤參照仍然允許;它適用於組織的 actions 與 GitHub 作者的 actions。
情境 C: 企業停用了某項政策,某組織管理員想自己啟用。
解析: 不行。企業停用的政策,個別組織與 repository 不能再啟用;企業啟用的政策才可以被下層選擇性停用。
來源:Disabling or limiting GitHub Actions for your organization(GH200-DOC-31)、Enforcing policies for GitHub Actions in your enterprise(GH200-DOC-32);2026-11-03 以頁面原文比對本節的數字與條件。兩頁對預設 GITHUB_TOKEN 權限的敘述略有差異(組織頁寫 contents 與 packages 唯讀;企業頁寫 2023-02-02 之後建立的預設全部範圍唯讀),本節兩者都寫出,沒有自行調和。
D4-O2 大規模管理 runners
GitHub 代管的 runners
- 標準的 GitHub 代管 runner 是 GitHub 託管的新 VM,每個 job 都是乾淨的環境;單一 CPU 的 runner 是例外,它在共用 VM 上的容器內執行。
- 預先安裝的軟體由
actions/runner-imagesrepository 管理,GitHub 擁有的映像每週更新。要知道某次執行實際有哪些工具,展開日誌中的 Set up job → Runner Image,「Included Software」連結列出清單;Windows 與 Ubuntu 映像也有軟體物料清單(SBOM)。 - 官方建議用 actions(例如
setup-*)而不是依賴預先安裝的工具,因為 actions 能選版本,映像更新時工具版本也比較穩定。 - 需要額外軟體時,在 job 裡安裝:Ubuntu 用
apt-get,先執行sudo apt-get update;macOS 用 Homebrew(brew update、brew install,GUI 用brew install --cask);Windows 用 Chocolatey(choco install)。 - **較大的 runner(larger runners)**提供更多核心或 GPU,用於 GitHub Team 與 GitHub Enterprise Cloud 方案;自訂映像只能搭配 larger runners,計費與該 runner 相同,映像儲存另外計入 Actions 儲存。
- 部分標準 2 核與 4 核 Linux runner 可以透過多雲運算(multi-cloud compute)在 AWS 或 Azure 上執行,這是選擇加入;啟用前要先更新 IP 允許清單。依賴網路允許清單或 Azure SDK 憑證的工作流程,換到另一個雲端可能失敗。Azure VM 上封鎖入站 ICMP,所以
ping與traceroute可能不能用。
自管(self-hosted)runner
自管 runner 是你部署與維護的系統:控制權較大,但作業系統與其他軟體都要自己更新;GitHub 只自動更新 runner 應用程式本身。它不需要每個 job 都是乾淨的執行個體,所以 job 之間的狀態可能殘留。有三種範圍:repository 層級(服務單一 repository)、組織層級(服務多個 repository)、企業層級(可指派給多個組織)。
支援的環境:Linux(RHEL 8+、Ubuntu 20.04+、Debian 10+ 等)、Windows(Windows 10/11 64 位元、Windows Server 2016/2019/2022)、macOS 11.0 以上;架構是 x64,ARM64 在 Linux、macOS、Windows 上為公開預覽。使用 Docker 容器 action 或服務容器的工作流程,需要裝有 Docker 的 Linux 機器。
連線需求:主機必須能對外建立 443 埠的 HTTPS 連線,至少 70 kbps 的上傳與下載速度;必要的網域有 github.com、api.github.com、*.actions.githubusercontent.com,其他依工作流程而定(例如下載 actions 用 codeload.github.com、容器用 ghcr.io)。如果組織或企業使用 IP 允許清單,必須把自管 runner 的 IP 位址加進去。
自動更新:預設啟用——指派 job 時更新,或 runner 沒有 job 時在新版釋出一週內更新;註冊時用 --disableupdate 可以停用。停用後,必須在新版釋出後 30 天內安裝新版,否則 GitHub 不再把 job 排給它;重大安全更新會擋住 job,直到更新為止。
暫時性(ephemeral)runner:註冊時用 --ephemeral,GitHub 在執行一個 job 後就取消註冊,清理由你負責;它的日誌要轉送到外部儲存。
job 如何選到 runner:job 會送給標籤與群組都符合 runs-on 的、線上且閒置的 runner;如果 runner 在 60 秒內沒有接起,job 會重新排隊;沒有符合的 runner 時 job 留在佇列,排隊超過 24 小時就會失敗。
runner 群組(runner groups)
- 每個組織有一個預設群組;註冊時沒指定群組的 runner 會進入預設群組,之後可以移動。
- GitHub Team 方案的組織擁有者可以建立額外的組織層級群組:Settings → Actions → Runner groups → New runner group。
- 建立群組時選擇存取政策:限定選定的 repository,或開放該組織所有 repository。
- 預設只有私有 repository 能使用群組內的 runner,可以覆寫;官方提醒自管 runner 最好只給私有 repository 使用,因為 fork 的 PR 可能在上面執行危險的程式碼。
- 移動 runner:在 Runners 開啟該 runner,從 Runner group 下拉選單選目的地;註冊時用
--runnergroup,但群組不存在時指令會失敗。 - 刪除群組前要先移走或移除群組內所有 runner;企業共用的群組,它的 repository 政策組織不能覆寫。
- 對 larger runners:組織層級的群組預設所有 repository 都可以存取,擁有者可以改成只開放選定的 repository;企業層級的群組預設沒有任何 repository 能存取,擁有者要逐一設定。組織或企業可以停用標準的 GitHub 代管 runner,讓工作流程必須使用 runner 群組。
larger runners 的網路
- 靜態 IP 位址範圍需要 GitHub Enterprise Cloud,macOS 的 larger runners 不支援;在每個 runner 上個別啟用,預設每個帳戶最多設定 10 個有靜態範圍的 runner 池,更多要聯絡 GitHub 支援。一個池的所有 job 共用同一個 IP 範圍,IP 數量不限制同時執行的 job 數。
- 官方提醒:使用固定 IP 範圍的 runner 只給私有 repository,因為 fork 的 PR 可能執行危險程式碼。
- **最大並行數(maximum concurrency)**是同時可執行的 job 上限,調高可以避免工作流程被平行度擋住;自動擴增出來的實例與原 runner 有相同的作業系統與硬體設定。
- 同區域內從 Azure 上 runner VM 連到 Azure Storage 帳戶,走的是私有 Azure IP,不是 runner 的公開 IP 範圍,所以用公開 IP 允許規則可能得到 403(AuthorizationFailure);官方不建議對 larger runners 用 Azure Storage 防火牆規則,除非使用靜態公開 IP 範圍或 Azure VNet。
監控與疑難排解自管 runner
| 狀態 | 意思 |
|---|---|
| Idle | 已連線,準備好執行 job |
| Active | 正在執行 job |
| Offline | 沒有連線:機器關機、runner 程式沒執行,或無法連到 GitHub |
- 日誌在 runner 安裝目錄的
_diag資料夾:Runner_檔案(每次程式啟動一份,以 UTC 時間命名)、Worker_檔案(每個 job 的詳細記錄)、SelfUpdate檔案(更新活動)。 - 服務狀態:Linux 用
systemctl --type=service | grep actions.runner,再用sudo journalctl -u <服務名稱> -f看即時活動;macOS 用./svc.sh status;Windows 在services.msc看服務,或用Get-Service。 - 網路連線檢查:
config --check搭配--url與--pat(需要workflow範圍的 classic PAT,或有 workflows 讀寫權限的 fine-grained PAT);每個服務回 PASS 或 FAIL,失敗的細節在_diag日誌。GITHUB_ACTIONS_RUNNER_TLS_NO_VERIFY=1只用於測試,官方不建議。 - runner 版本低於最低版本時 job 不會執行,在
Runner_日誌找「An update is available」。 - 要跑 Linux 容器 job,Docker 必須已安裝並在執行(
sudo systemctl is-active docker.service);/var/run/docker.sock出現 permission denied 時,檢查服務帳號的 Docker 權限。 - 這次沒有核對到:標籤不符的排錯、離線一段時間後的自動移除(官方常見說法是持續型 14 天、暫時性 7 天,但我讀的頁面沒有寫)、GitHub 代管 runner 的 IP 範圍與每週變動、組織 IP 允許清單頁面(網址回 404)。
判斷練習
情境 A: 一個 job 的 runs-on: [self-hosted, linux, gpu],卻一直排隊,最後失敗。
解析: 沒有符合所有標籤與群組、線上且閒置的 runner;排隊超過 24 小時就會失敗。檢查標籤是否完全相符、runner 是否在線(不是 Offline),以及該 repository 是否有權使用該 runner 群組。
情境 B: 你停用了自管 runner 的自動更新,一個月後 job 全部不再被排到那些 runner。
解析: 停用自動更新後,新版釋出後 30 天內必須安裝,否則 GitHub 停止排 job 給它;需要手動升級 runner 應用程式。
情境 C: 組織有 IP 允許清單,新的自管 runner 註冊後連不上 GitHub。
解析: 必須把自管 runner 的 IP 位址加進允許清單;同時確認出站 443 與必要網域(github.com、api.github.com、*.actions.githubusercontent.com)可連。
來源:About GitHub-hosted runners(GH200-DOC-41)、Customize GitHub-hosted runners(GH200-DOC-42)、About self-hosted runners(GH200-DOC-34)、Self-hosted runners reference(GH200-DOC-35)、Manage access to self-hosted runners(GH200-DOC-33)、Monitor and troubleshoot self-hosted runners(GH200-DOC-36)、Manage larger runners(GH200-DOC-43);2026-11-03 以頁面原文比對本節的數字與條件。runner 群組頁面沒有涵蓋「限定特定工作流程」,本節沒有補寫。
D4-O3 管理加密 secrets 與 variables
三個層級與存取
- Secrets 與 variables 都可以設在組織、repository、環境(environment)三個層級。GitHub Actions 只有在工作流程明確引用時才能讀取 secret。
- 組織 secrets 可以共用給多個 repository,並用存取政策限制哪些 repository 能使用:所有 repository、僅私有 repository、或選定的 repository。更新一個組織 secret,所有使用它的工作流程都跟著更新。GitHub Free 的組織層級 secrets 與 variables 不能被私有 repository 存取。
- 同名的環境 secret 優先於 repository 或組織 secret。
- 環境 secret 若啟用了必要審核者(required reviewers),job 在核准之前無法存取環境 secret。
- 建立權限:組織 repository 的 repository secrets 需要寫入權限(個人 repository 要是協作者);環境 secrets 在組織 repository 需要 admin(個人 repository 要是擁有者);組織 secrets 由組織擁有者設定。variables 也相同:組織 repository 的 repository variables 需要寫入權限,環境 variables 需要 admin。
secret 與 variable 的差別
| 項目 | secrets | variables(設定變數) |
|---|---|---|
| 讀取方式 | secrets context |
vars context |
| 未設定時 | 評估成空字串 | 評估成空字串 |
| 日誌 | 自動遮蔽,但官方說不保證,且只能遮蔽目前 job 用到的 secret | 我讀的頁面沒有寫是否遮蔽(不要把機密放在 variables) |
| 加密 | 在送到 GitHub 之前就以 Libsodium sealed box 在用戶端加密 | — |
variables 與 env 不同:env 在工作流程、job 或步驟層級設定,作用範圍就是該元素;設定變數在介面建立,用 vars 讀取。官方把「在組織層級設預設值、讓 repository 擁有者覆寫」當成使用情境,但沒有寫出明確的優先順序,我也沒有核對到名稱限制、大小與數量上限。
使用 secrets 的規則
- 傳給 action:用
with:當輸入,或用env:當環境變數,例如${{ secrets.SuperSecret }}。 - fork 的工作流程:從 fork 觸發的工作流程,除了
GITHUB_TOKEN之外不會傳遞 secrets;Dependabot 事件觸發的工作流程也拿不到 secrets。 - 可重用工作流程:secrets 不會自動傳遞,要明確傳入或用
secrets: inherit(見領域一)。 - 不能直接用在
if::把它放進 job 層級的環境變數再判斷。 - 避免在命令列傳遞:其他使用者可能在行程清單看到;改用環境變數或標準輸入。
- 遮蔽非 secret 的敏感值:用
::add-mask::。 - 大型 secret:超過 48 KB 的內容,官方的做法是用
gpg(AES256)加密檔案、把.gpg檔提交到 repository,並把密碼存成 secret,工作流程再解密;官方警告:用這個做法時,GitHub 不會遮蔽印到日誌中的內容。小型二進位檔可以存成 Base64 編碼的 secret,在 runner 上解碼。 - 最小權限:盡量給最少權限,優先用部署金鑰、服務帳號或 GitHub App,而不是個人憑證,可以的話用 fine-grained 權杖。
用 REST API 管理
| 範圍 | 建立或更新 | 取得公鑰 |
|---|---|---|
| 組織 | PUT /orgs/{org}/actions/secrets/{secret_name}(需 encrypted_value、key_id、visibility) |
GET /orgs/{org}/actions/secrets/public-key |
| repository | PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}(需 encrypted_value、key_id) |
GET /repos/{owner}/{repo}/actions/secrets/public-key |
| 環境 | PUT /repos/{owner}/{repo}/environments/{environment_name}/secrets/{secret_name} |
GET .../environments/{environment_name}/secrets/public-key |
- 列出(
GET)與刪除(DELETE)用相同的路徑;GET /repos/{owner}/{repo}/actions/organization-secrets列出共用給該 repository 的組織 secrets;組織 secret 的.../repositories端點管理可用的 repository。 - secret 的值必須用 LibSodium 以公鑰加密,
key_id要與該公鑰一致。 - 權限:組織端點需要 collaborator 存取,classic 權杖與 OAuth 權杖要有
admin:org範圍(私有 repository 再加repo);repository 與環境端點的 classic 與 OAuth 權杖需要repo範圍;公鑰端點只需要對該 repository 的讀取權限(私有 repository 才需要repo)。官方沒有在這頁寫 fine-grained 權杖需要的權限。 - variables 的 REST 端點,我讀的 secrets 頁面沒有涵蓋,備考前請在官方 REST 頁面確認。
判斷練習
情境 A: 同名的 API_KEY 同時存在於組織、repository 與 production 環境,工作流程的 job 指定 environment: production。
解析: 環境 secret 優先於 repository 與組織 secret。若環境設了必要審核者,job 要等核准後才拿得到它。
情境 B: 你要在 if: 判斷某個 secret 是否存在,寫了 if: secrets.TOKEN != '',工作流程無效。
解析: jobs.<id>.if 與 steps.if 可用的 context 沒有 secrets。把它放進 job 層級的環境變數(env: HAS_TOKEN: ${{ secrets.TOKEN != '' }}),再用 env 判斷。
情境 C: 平台團隊要用腳本替一百個 repository 設定同一個 secret。
解析: 先取得各端點的公鑰與 key_id,用 LibSodium 加密後 PUT 到對應端點;若這個 secret 是共用的,更適合設成組織 secret 並用存取政策限定 repository,更新一次就套用到全部。
來源:Using secrets in GitHub Actions(GH200-DOC-39)、About secrets(GH200-DOC-37)、Use variables(GH200-DOC-38)、REST: secrets(GH200-DOC-40)、Reuse workflows(GH200-DOC-08);2026-11-03 以頁面原文比對本節的數字與條件。同名 secret 在三層級之間的優先順序,只有「環境優先於 repository 或組織」一句來自可重用工作流程頁面;secret 的命名規則、單一 secret 的大小上限與各層級的數量上限,我讀的頁面沒有寫,本節沒有補寫。