Skip to content

Reviewed plan

The person who approves a change and the process that carries it out need not be the same. This example is written for that split.

octoform.yml
# A configuration written to be planned by one party and applied by another.
#
# Nothing in the YAML enables that separation — the separation comes from how
# the two commands are run:
#
#   # reviewer, read-only credentials
#   octoform plan  --config octoform.yml --repo example-service \
#                  --out plan.json --expires-in 30
#
#   # applier, write credentials, after the plan has been reviewed
#   octoform apply --plan plan.json
#
# The second command re-checks the artifact schema version, the expiry, the
# authenticated actor, each account's numeric identity, every configuration
# source file, and the resolved configuration itself. If any of them moved it
# refuses and names which one. That is why this example declares settings a
# reviewer would genuinely want to look at before they are applied.
#
# `example-org` is a placeholder — replace it before running anything.

version: 1

owners:
  example-org:
    defaults:
      security:
        secret_scanning: true
        secret_scanning_push_protection: true

    repos:
      example-service:
        merge:
          delete_branch_on_merge: true
        default_branch:
          name: main
        rulesets:
          - name: protected-default
            target_branches: ['~DEFAULT_BRANCH']
            required_approvals: 1
            required_checks: ['build']
            block_force_push: true

Download YAML

The two commands

octoform plan  --config octoform.yml --repo example-service --out plan.json --expires-in 30
octoform apply --plan plan.json

The first can run with read-only credentials. The second needs write access, and performs exactly the operations the file records — it does not re-plan and does not re-observe.

What the second command checks first

Before it touches anything it re-checks six things, and refuses naming which one failed if any of them moved:

Check A failure means
Artifact schema version The file came from an incompatible release
Expiry The observation behind the plan is too old to act on
Authenticated actor A different identity is applying than planned
Account numeric identity The account was renamed or replaced
Configuration source digests A contributing file changed after review
Resolved configuration digest The document now means something different

A stale plan is never silently repaired and never quietly re-planned into something else. The remedy is always to plan again, review the new plan, and hand over the new artifact.

Treat the artifact like the configuration

It carries no credential, but it does name private repositories and their settings. It is not a build-log attachment.

Why the settings here are worth reviewing

A saved plan is only useful if someone actually reads it. This configuration declares a ruleset with required approvals, required checks, and force-push blocking on the default branch — the kind of change where a reviewer will want to see the exact current and desired values before anyone applies it.

The expiry is deliberately short. Thirty minutes is enough for a review and short enough that an unattended artifact stops being applicable.

In automation

This is the shape to prefer when a workflow applies changes. The approval that apply's interactive prompt would have represented becomes the review of the plan artifact, and --yes no longer stands in for a review that never happened. See the automation guide.