Skip to content

CI/CD automation

The automation architecture view shows how pull-request planning, scheduled observation, and protected apply must use different trust boundaries.

Automation should make drift visible more often than it changes state. Begin with a scheduled read-only job and introduce apply only after the policy, repository selection, credentials, and approval boundary are established.

Scheduled read-only audit

.github/workflows/governance-audit.yml
name: Repository governance audit

on:
  workflow_dispatch:
  schedule:
    - cron: '17 6 * * 1'

permissions:
  contents: read

jobs:
  audit:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npx --yes @hector21/octoform@0.5.0 audit --config octoform.yml
        env:
          GITHUB_TOKEN: ${{ secrets.OCTOFORM_AUDIT_TOKEN }}

audit exits 1 when it reports findings, so the scheduled job fails on drift and the failure is the notification. On the 0.3 line it exited 0 regardless; a job carried over from that line starts failing the first time it finds something, which is the intended behaviour rather than a regression.

Credential selection

Credential Appropriate use Boundary
Workflow GITHUB_TOKEN Repositories visible to that workflow's repository token Usually insufficient for an owner-wide inventory
Fine-grained PAT Initial or narrowly scoped automation Long-lived secret; restrict repositories and permissions
GitHub App installation token Production owner-wide automation Short-lived and installation-scoped; preferred

Never print the token, enable shell tracing around it, or expose private repository names through a public workflow log.

Protected apply

Octoform 0.5 can hand a reviewed plan from one job to another. plan --out writes a versioned artifact and apply --plan performs exactly the operations it records, refusing with a named reason when the actor, an account's numeric identity, the configuration digest, a source digest, or the expiry no longer match. This is the shape to prefer: the job that mutates applies what was reviewed, rather than re-deriving it and hoping the two agree.

The artifact is not cryptographically signed and does not try to be. It detects a plan that no longer matches the world it was made in, not an attacker who can rewrite files in the workspace — it carries the same trust as the configuration file beside it. A protected job must therefore still approve the workflow revision and the policy, not the plan file alone.

Running apply without --plan plans, displays, and mutates in the same process, which remains correct for interactive use.

Use all of these controls:

  • manual workflow_dispatch, never an unreviewed pull-request workflow;
  • a protected environment with required reviewers;
  • a GitHub App or narrowly scoped token stored only in that environment;
  • a pinned Octoform patch;
  • an explicit --repo or reviewed --type selector;
  • concurrency that prevents simultaneous governance runs;
  • retained logs with secret masking and restricted access.
.github/workflows/governance-apply.yml
name: Apply repository governance

on:
  workflow_dispatch:
    inputs:
      repository:
        description: Exact repository name
        required: true
        type: string

permissions:
  contents: read

concurrency:
  group: octoform-apply
  cancel-in-progress: false

jobs:
  apply:
    environment: governance-apply
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npx --yes @hector21/octoform@0.5.0 apply --yes --config octoform.yml --repo "$TARGET_REPOSITORY"
        env:
          GITHUB_TOKEN: ${{ secrets.OCTOFORM_APPLY_TOKEN }}
          TARGET_REPOSITORY: ${{ inputs.repository }}

The example uses version tags for readability. A production supply-chain policy may additionally pin Actions to reviewed commit SHAs.

Patch shown in commands

The documentation line is 0.5. Executable examples pin 0.5.0, the latest patch verified by this publication. Review the changelog before adopting a later patch.

Failure and recovery

If apply exits non-zero, assume partial success is possible. Preserve the log, run a fresh read-only plan, and reconcile the remaining state. Do not blindly rerun a failed workflow before reviewing the new plan.