Bring an organization under governance¶
Governing repositories is additive: a policy that manages three settings leaves everything else alone. Governing the account is not, because some of its settings are floors and ceilings under repositories the file never names.
This guide is an order to introduce it in. Each step is reviewable on its own and each one tells you something you need before the next.
Before anything: what is there¶
The first proves the account is an organization, gives you its numeric identity, and reports any declaration in your file that does not apply to it.
The second is the one people skip, and it is the one that saves work later:
Organisation: example-org
owners: example-lead
members: example-dev-a, example-dev-b
outside collaborators: example-contractor
without two-factor authentication: not visible to this token
invited and waiting:
example-newcomer as direct_member, 61 day(s)
named by the configuration but not in the organisation:
example-reviewer: organization.teams.reviewers.membership.members
Two things to act on before writing any more policy. An invitation waiting 61
days is not going to be accepted; withdraw it. And example-reviewer cannot be
put on a team until they are in the organization, so that line will block on
every run until somebody invites them.
Step 1: the profile¶
Start with the part that reaches nothing.
organization:
profile:
description: Platform engineering for the example estate
website: https://example.invalid
Every profile field is normal risk. This step exists to confirm the token,
the account and the file, not to change anything important.
If the plan reports the change as blocked with could not read the current organisation settings, the token cannot administer the organization. Fix that before continuing, because everything after this needs more, not less.
Step 2: teams, before the things that name them¶
organization:
teams:
platform:
name: Platform
privacy: closed
platform-oncall:
parent: platform
privacy: closed
membership:
maintainers: [example-lead]
members: [example-dev-a]
Teams first, because roles and repository grants both refer to them by slug, and a reference to a team that does not exist blocks.
Leave authoritative alone at this stage. Additive membership cannot remove
anybody, which is what you want while the lists are still being written from
memory.
Read the plan for one thing in particular: a team you thought existed appearing
as a creation. That means the slug in your file is not the slug GitHub
uses, and the fix is rename_from,
not a second team.
Step 3: property definitions, if you use them¶
organization:
properties:
tier:
value_type: single_select
allowed_values: [bronze, silver, gold]
default_value: bronze
Definitions before any organization ruleset that selects on them, and before the repository values that answer them.
If the plan blocks every definition with could not read the current custom property definitions, that is not a per-property problem: the write endpoint replaces, so a definition that cannot be read cannot be written without discarding fields nobody looked at.
Step 4: base_permission, deliberately¶
This is the step to slow down on.
base_permission is a floor. Every member holds at least that level on every
repository the organization owns, including repositories no policy names.
Lowering it takes access away from people who never appeared in any file.
Before applying it, know who loses what. inspect members gives you the
members; the repositories they were reaching through the floor are the ones no
access block mentions.
The safe order is to add the explicit grants first, confirm them, and lower the floor in a separate run:
Step 5: organization rulesets, in evaluate¶
organization:
rulesets:
- name: watched-default-branches
enforcement: evaluate
target_branches: ['~DEFAULT_BRANCH']
block_force_push: true
repositories:
properties:
- name: tier
values: [gold]
evaluate reports what would have been blocked without blocking it. For a
ruleset selected by property, that is the only honest way to find out what it
actually covers — the condition matches whatever answers tier: gold today,
including a repository created tomorrow.
Leave it evaluating for a week. Then set active.
Step 6: roles, last¶
Roles last because they refer to teams, and because both granting and revoking
one is sensitive: an organization role reaches every repository the account
owns.
A role name that matches nothing is refused as a name. GitHub has no endpoint that creates an organization role, so Octoform cannot define one for you.
When to turn authoritative on¶
Not until the lists have been correct for several runs.
authoritative: true on a team membership
or a role is what asks for removal. Two guards
protect you from the obvious mistakes — a list naming nobody, and a list that
would remove the account you are authenticated as — and neither protects you
from a list that is simply out of date.
Turn it on one team at a time, and read the detach lines in the plan before
confirming.
What stays outside the file¶
Organization membership itself. Adding and removing people is three commands, one person per invocation, each stating what it will do and asking first.
That is not an omission waiting to be filled in a later release. An invitation is an act addressed to a person who is emailed about it, and a file that listed members authoritatively would remove somebody the first time a name was mistyped.
If something goes wrong¶
There is no rollback command, and for the owner-level settings a second run recovers most but not all of it. See incidents and recovery, particularly recovering from an owner-level change.