From 3777418ab2a94f1522dbe628cd040726c2ba1480 Mon Sep 17 00:00:00 2001 From: Elior Erez Date: Wed, 9 Sep 2026 00:56:40 +0300 Subject: [PATCH] Add infrastructure team for runner management and merge-queue bypass Two contributors need to manage the org's self-hosted Actions runners and to force-merge past the merge queue / required checks on the mono-repo, without being made organization owners. Today the only path to either is membership in the broad wg-infra working group. This adds a small, purpose-built team instead. New "infrastructure" team (teams.csv, team-members/infrastructure.csv): omer-vishlitzky and minmzzhang. Runner management (organization.tf): assign GitHub's predefined "CI/CD admin" organization role to the team. It covers self-hosted runners and runner groups plus Actions policies, secrets, variables, and usage metrics -- a superset of the existing runner-manager custom role, which stays assigned to wg-infra unchanged. The role id is resolved at plan time from the github_organization_roles data source by matching the predefined role name; if that lookup comes back empty, confirm the exact name/id with: gh api /orgs/osac-project/organization-roles \ --jq '.roles[] | select(.source=="Predefined") | {id, name}' and pin role_id directly. Merge-queue bypass (repositories.tf): add the team to ruleset_bypass_team_ids on osac and osac-test-infra (alongside wg-infra, which keeps its bypass), and give it push on both repos so bypass actors can actually merge. Scope is deliberately just these two repos, not the archived component repos that also list wg-infra. Verified locally: tofu fmt -check clean; tofu validate passes with provider 6.12.1 (only the pre-existing has_downloads deprecation warnings). tofu plan not run here -- it needs the S3 state credentials and an admin:org token, which run in CI. Co-Authored-By: Claude Sonnet 5 --- README.md | 15 +++++++++++++++ organization.tf | 26 ++++++++++++++++++++++++++ repositories.tf | 20 +++++++++++++++++--- team-members/infrastructure.csv | 3 +++ teams.csv | 1 + 5 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 team-members/infrastructure.csv diff --git a/README.md b/README.md index f3c9b88..d13c0a2 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,21 @@ When a commit is pushed to the `main` branch (e.g., when a pull request merges), 1. Open `team-members/.csv` 1. Add a new line of the form `,`, where `` should be `member`. +### Give someone runner management or merge-queue bypass without making them an org owner? + +Add them to the `infrastructure` team (`team-members/infrastructure.csv`). That team: + +- Holds GitHub's predefined **CI/CD admin** organization role, granting management of + org self-hosted runners and runner groups (plus Actions policies, secrets, variables, + and usage metrics). See `organization.tf`. +- Is a ruleset bypass actor on `osac` and `osac-test-infra`, so its members can bypass + the merge queue and required status checks on those repos. See `ruleset_bypass_team_ids` + in `repositories.tf`. + +The broader `wg-infra` working group keeps the narrower `runner-manager` role and the same +bypass; `infrastructure` is a smaller team for people who need this without the rest of the +working-group context. + ### Add a new repository? 1. Open `repositories.tf` diff --git a/organization.tf b/organization.tf index 823502d..d96a590 100644 --- a/organization.tf +++ b/organization.tf @@ -27,3 +27,29 @@ resource "github_organization_role_team" "runner_manager_wg_infra" { role_id = github_organization_role.runner_manager.role_id team_slug = github_team.all["wg-infra"].slug } + +# Assign GitHub's predefined "CI/CD admin" organization role to the infrastructure +# team. It grants admin access to Actions policies, runners, runner groups, hosted +# compute network configs, secrets, variables, and usage metrics for the org -- a +# superset of the runner-manager role above -- without granting org-owner access. +data "github_organization_roles" "all" {} + +locals { + # Predefined roles are returned with source = "Predefined". The API's exact + # spelling for this role's `name` isn't documented (GitHub's own docs example + # for the analogous "security manager" role uses the slug "security_manager"), + # so match the known spellings exactly rather than with a loose regex. If the + # lookup comes back empty, get the real value and pin role_id directly: + # gh api /orgs/osac-project/organization-roles --jq '.roles[] | select(.source=="Predefined") | {id, name}' + cicd_admin_role_names = ["CI/CD admin", "cicd_admin", "ci_cd_admin"] + + cicd_admin_role_id = one([ + for r in data.github_organization_roles.all.roles : r.id + if r.source == "Predefined" && contains(local.cicd_admin_role_names, r.name) + ]) +} + +resource "github_organization_role_team" "cicd_admin_infrastructure" { + role_id = local.cicd_admin_role_id + team_slug = github_team.all["infrastructure"].slug +} diff --git a/repositories.tf b/repositories.tf index 5939ae7..eb30388 100644 --- a/repositories.tf +++ b/repositories.tf @@ -142,6 +142,10 @@ module "repo_osac" { { team_id = "wg-infra" permission = "admin" + }, + { + team_id = "infrastructure" + permission = "push" } ] # Prow plugins handle approval via OWNERS (lgtm/approved labels), not native @@ -184,7 +188,10 @@ module "repo_osac" { { context = "Run unit tests (osac-operator)", integration_id = 15368 }, { context = "Run unit tests (bare-metal-fulfillment-operator)", integration_id = 15368 }, ] - ruleset_bypass_team_ids = [github_team.all["wg-infra"].id] + ruleset_bypass_team_ids = [ + github_team.all["wg-infra"].id, + github_team.all["infrastructure"].id, + ] merge_queue = { merge_method = "SQUASH" @@ -328,6 +335,10 @@ module "repo_osac_test_infra" { { team_id = "wg-infra" permission = "admin" + }, + { + team_id = "infrastructure" + permission = "push" } ] required_approvals = null @@ -341,8 +352,11 @@ module "repo_osac_test_infra" { # check_response_timeout_minutes for a check that never reports. { context = "pre-commit", integration_id = 15368 }, ] - ruleset_bypass_team_ids = [github_team.all["wg-infra"].id] - environments = [{ name = "e2e-test" }] + ruleset_bypass_team_ids = [ + github_team.all["wg-infra"].id, + github_team.all["infrastructure"].id, + ] + environments = [{ name = "e2e-test" }] merge_queue = { merge_method = "SQUASH" diff --git a/team-members/infrastructure.csv b/team-members/infrastructure.csv new file mode 100644 index 0000000..127ebee --- /dev/null +++ b/team-members/infrastructure.csv @@ -0,0 +1,3 @@ +username,role +omer-vishlitzky,member +minmzzhang,member diff --git a/teams.csv b/teams.csv index 9fe720a..4a619a3 100644 --- a/teams.csv +++ b/teams.csv @@ -5,3 +5,4 @@ observability-wg,observability working group,secret personas-wg,Personas working group,secret documentation-wg,Documentation working group,secret wg-infra,OSAC Infrastructure working group,closed +infrastructure,Infrastructure & CI/CD operations - runners and merge-queue bypass,closed