Skip to content

The organisation's rulesets and its quality_gate property are declared in platform #219

Closed

Description

@FlexOr2

The three organisation rulesets and the quality_gate property schema are declared in src/opentofu/github and imported from code, so no ruleset lives only in the web UI.

Caller

Neighbours: G3 (the scheduled drift check, later under #10), which reads what this slice declares; quality, whose docs/gate.md still describes the ruleset and whose item follows this landing.

Today (API read, 08.11.2026)

Organisation overnightworks, all three active, read them with gh api orgs/overnightworks/rulesets/<id>:

  • 25224011 main: PR only (all repositories), target branch
  • 25227067 quality / verdict required, target branch
  • 25224012 release tags are immutable (all repositories), target tag

Property schema (gh api orgs/overnightworks/properties/schema): quality_gate, true_false, values editable by organisation actors. The schema also holds role (single_select), which the contract does not cover and this slice leaves undeclared, unchanged.

The contract with quality (quality's head and platform's head, 06.11.2026)

  1. The check's name is quality's. The ruleset requires the context quality / verdict from GitHub Actions, written exactly, with no pattern, and pinned to GitHub Actions with integration_id = 15368. Today's ruleset has no pin; quality's head agreed to it, since gate.yml is the only producer and quality's merge command posts no status or check run.
  2. The targeting stays on the property. The ruleset reaches a repository only through quality_gate=true. Type true_false, values editable by organisation actors, which the operator's login is. quality's merge command flips the value per repository, so the values are never declared here: only the schema.
  3. The bypass list stays empty. quality's merge command is the one deliberate way past, logged on the pull request.
  4. Enforcement stays active.

What binds

  • The three rulesets and the property schema are declared in a child module src/opentofu/github/organization/, and the root holds its call and the import blocks, so a mocked test can plan the module directly (tofu test cannot plan a resource an import block names).
  • scripts/apply_github_settings.sh on today's organisation, on this lane's commit, plans exactly four imports and exactly one change: integration_id = 15368 added to the quality / verdict check. It adds and destroys nothing. Any other difference is matched in the declaration to what the organisation holds today, or reported and not silently accepted.
  • Every ruleset carries lifecycle { prevent_destroy = true }, and every resource of the module destroy = false as G1's do, so that removing a block forgets the object and never deletes it. OpenTofu ignores prevent_destroy once destroy = false holds in state, and the rulesets keep the line as ruled.
  • A mocked tofu test asserts the four points above on the planned resources of the module: the exact context with no pattern and the integration id, targeting only through the property with no repository name or id condition, the empty bypass list and enforcement active on all three rulesets, and the schema's type and who may edit its values. It also asserts the required approving review count of main: PR only stays 0, naming the field below.
  • The root holds no token, the plan and apply stay the operator's own gh login, and the command's existing refusals (undeclared listing entries, a dirty or foreign checkout for --apply) still hold. The command refuses any plan that would delete an object, naming its address, whether the plan is only shown or applied, and applies nothing. An imported object that the first apply leaves unchanged holds its destroy = false in state only after a later apply that changes it, so the lifecycle alone cannot be relied on. A deliberate removal uses a removed block with destroy = false.

Paths traced

src/opentofu/github/main.tf (providers), repositories.tf (the pattern), tests/repository_listing.tftest.hcl (mock provider and import blocks under it), scripts/apply_github_settings.sh (plans the whole root, and gains the refusal above). .terraform.lock.hcl pins integrations/github 6.13.0 and stays unless that version cannot represent one of these objects, in which case the builder says so before changing it. mise run github:test and the job opentofu already gate the root.

Unknowns the build settles

  • Whether provider 6.13.0 has a resource for the property schema, or the existing pattern must use another. Name what was checked.
  • Whether the operator's gh login has the scope to write organisation rulesets. Reading them works. A refused apply becomes the operator's one-line gh auth refresh, never a workaround.

Known limits of provider 6.13.0, named in the declaration and AGENTS.md

  • require_extra_approval_for_unattributed_changes = true on main: PR only cannot be carried: import and this apply leave it alone, and a later update of that ruleset sends it not at all, whether GitHub then keeps it is unknown without a write. It takes effect only at a non-zero required approval count, which is 0, so the test above holds the count at 0 and a change that raises it must first get a provider that carries the field. Head's decision, against a broader refusal, since the setting is inert while the count is 0.
  • quality_gate's default is undeclared because the provider returns none for a true_false property. A later update of the schema resource would send an empty default, with GitHub's answer unknown; G3 does not see this default.

Not in this slice

The apply, which an agent runs from a clean origin/main after this lands, once quality's head has been told of the integration_id line. The landing dispatches quality's item to point docs/gate.md at platform's definition. G3 is its own slice.

Material risk: organisation-wide security settings. The final gate runs on another provider than the build.

version = 1
now = "Cut on 08.11.2026 from #10's slice G2."
next = "A builder declares the three rulesets and the property schema and shows the plan."
done_when = "The three organisation rulesets and the quality_gate property schema are declared and imported from code, a plan on today's organisation shows exactly four imports and one change (integration_id 15368 on the quality / verdict check), every ruleset carries prevent_destroy, and a mocked tofu test asserts the four points of the contract with quality."

scope = ["AGENTS.md", "mise.toml", "scripts/apply_github_settings.sh", "src/opentofu/github/organization", "src/opentofu/github/organization.tf", "src/opentofu/github/tests/organization_rulesets.tftest.hcl", "tests/github"]

size = "M"

whole = "A ruleset module, its root call and imports and the test that plans it are one declaration with one proof."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions