Skip to content

Organization

The account above the repositories, declared as state and reviewed in the same plan. This is the shape the 0.5 line introduced.

octoform.yml
# Governing the account itself, not only its repositories — the shape 0.5
# introduced.
#
# Everything under `organization` describes the account above the repositories:
# its public identity, what its members may do without asking, the custom
# properties it defines, and the rulesets it aims at repositories it selects.
#
# None of it has a command of its own. It appears in the same plan as the
# repository changes, grouped under a heading that names the account, and is
# confirmed the same way:
#
#   export GITHUB_TOKEN=...
#   octoform plan  --config octoform.yml
#   octoform apply --config octoform.yml
#
# The account is brought into agreement with this file before any repository is
# touched, so a lowered base permission is never briefly wider than it says.
#
# `example-org` is a placeholder. Replace it before running anything.

version: 1
owner: example-org

organization:
  profile:
    description: Platform engineering for the example estate
    website: https://example.invalid
    location: Remote

  # Eight of these reach every repository the organisation owns, including
  # ones this file never names, so the plan reports them as sensitive.
  # `base_permission` is a floor: no repository policy can go below it.
  members:
    base_permission: read
    create_public_repositories: false
    create_private_repositories: true
    fork_private_repositories: false
    web_commit_signoff_required: true

  # The organisation *defines* a property here. A repository *answers* it under
  # its own `properties`, further down. The two are not interchangeable.
  #
  # The definition endpoint replaces rather than patches, so octoform reads the
  # current definition and carries forward every field this file omits.
  properties:
    tier:
      value_type: single_select
      description: How closely this repository is watched
      allowed_values: [bronze, silver, gold]
      default_value: bronze
      values_editable_by: org_actors

  # Aimed at whatever the condition matches — which can include repositories
  # this file never lists. Every organisation ruleset is reported as sensitive
  # for that reason.
  #
  # Run it once with `enforcement: evaluate`. That reports what would have been
  # blocked without blocking it, and it is the only honest way to find out what
  # a property condition actually covers.
  rulesets:
    - name: watched-default-branches
      enforcement: evaluate
      target_branches: ['~DEFAULT_BRANCH']
      block_force_push: true
      block_deletion: true
      require_pull_request: true
      required_approvals: 1
      repositories:
        properties:
          - name: tier
            values: [gold, silver]

defaults:
  merge:
    delete_branch_on_merge: true

types:
  service:
    # The repository's half of the property defined above. Changing this can
    # change which organisation rulesets reach the repository, which is why a
    # property value is sensitive too.
    properties:
      tier: gold

repos:
  example-service:
    type: service
  example-sandbox:
    properties:
      tier: bronze

Download YAML

What the plan looks like

Organization changes are grouped under a heading that cannot be mistaken for a repository name — a repository really can be called the same thing as the organization that owns it:

5 change(s) across 2 repositories:

  (example-org: the organisation itself)
    organization.profile.description: (unset) -> Platform engineering for the example estate
    organization.members.base_permission: write -> read
    organization.properties.tier: (unset) -> tier: single_select; bronze, silver, gold
    organization.rulesets.watched-default-branches: (unset) -> evaluate on ~DEFAULT_BRANCH; ...

  example-service
    properties.tier: (unset) -> gold

The owner-level work is applied first, and the organization ruleset last within it, because it reaches furthest. See owner reconciliation.

The order to introduce this in

Do not apply all of it at once against an organization that has repositories in it. The three steps below each have a different failure mode, and separating them is what makes each one reviewable.

1. The profile alone

octoform plan --config octoform.yml

Comment out members, properties and rulesets for the first run. The profile is normal risk and reaches nothing; it is the cheapest way to confirm the token, the account and the file all work.

2. members, read carefully

base_permission is a floor under every repository the organization owns, including repositories this file never names. Lowering it takes access away from people who never appeared in any policy.

Before applying it, find out who that is:

octoform inspect members --config octoform.yml

3. The ruleset, in evaluate first

The example ships with enforcement: evaluate on purpose. A property condition covers whatever currently answers tier: gold — including repositories added tomorrow — and evaluate reports what would have been blocked without blocking it.

Switch it to active once you have seen a week of that.

Definitions and values are different halves

organization.properties.tier defines the property. properties.tier under types.service answers it. Neither can stand in for the other, and the same word twice is GitHub's.

The definition endpoint replaces rather than patches, so octoform reads the current definition and carries forward every field this file does not mention. That is why correcting a description does not silently reset who may edit the values — which is exactly what earlier releases did.

What is not in this file

Membership of the organization itself. That is three commands, not a declaration, and this example deliberately leaves it out: a file that listed members authoritatively would remove somebody the first time a name was mistyped.

See the teams and access example for who is on which team and what each team may reach.