Skip to content

docs: required destination checks can be set via the rulesets API before their first run #66

Description

@jmgilman

Problem

docs/how-to/add-homebrew-and-scoop.md
tells adopters that a required check cannot be added until it has run once, and
routes them through a first publication against an unprotected destination:

GitHub may not offer the required check until it has run once. If necessary,
enable the publisher for one real release, wait for the new tap pull request
and its validation run, add that check to the ruleset, and only then merge the
pull request.

That is true of the UI picker, which only offers observed checks. It is not
true of the rulesets REST API, which accepts an arbitrary context string. The
destination can therefore be fully protected before the first publisher run.

Evidence

Both rulesets below were created on brand-new repositories with zero workflow
runs and zero check history, and were immediately active:

gh api -X POST repos/componere/homebrew-tap/rulesets --input - <<'JSON'
{
  "name": "Default branch",
  "target": "branch",
  "enforcement": "active",
  "bypass_actors": [{"actor_id": 5, "actor_type": "RepositoryRole", "bypass_mode": "always"}],
  "conditions": {"ref_name": {"include": ["~DEFAULT_BRANCH"], "exclude": []}},
  "rules": [
    {"type": "deletion"},
    {"type": "non_fast_forward"},
    {"type": "pull_request", "parameters": {
      "required_approving_review_count": 0,
      "dismiss_stale_reviews_on_push": false,
      "require_code_owner_review": false,
      "require_last_push_approval": false,
      "required_review_thread_resolution": false,
      "automatic_copilot_code_review_enabled": false,
      "allowed_merge_methods": ["squash"]
    }},
    {"type": "required_status_checks", "parameters": {
      "strict_required_status_checks_policy": false,
      "do_not_enforce_on_create": false,
      "required_status_checks": [
        {"context": "casks / Homebrew cask validation", "integration_id": 15368}
      ]
    }}
  ]
}
JSON

Result: ruleset 21201639 on componere/homebrew-tap and 21201641 on
componere/scoop-bucket, with contexts casks / Homebrew cask validation and
manifests / Scoop manifest validation.

The context string is <caller job id> / <reusable result job name>. Both halves
are fixed by this repository, so they are knowable in advance:

  • init homebrew-tap generates job id casks, and
    homebrew-tap-ci.yml names its final job Homebrew cask validation.
  • init scoop-bucket generates job id manifests, and
    scoop-bucket-ci.yml names its final job Scoop manifest validation.

integration_id: 15368 is the GitHub Actions app, matching what
meigma/homebrew-tap's own live protection already records for the identical
context.

Why this matters

The documented order enables a publisher against a destination whose main has
no PR requirement and no required check, for the duration of the first release.
The API order protects the destination first, so the first tap or bucket pull
request lands in an already-enforced repository.

Suggested change

  1. In add-homebrew-and-scoop.md, make the API call the primary instruction for
    both destinations, with the exact context strings, and keep the UI
    "run it once" note as a fallback for adopters who prefer the picker.
  2. Consider having release-cli init homebrew-tap / init scoop-bucket emit the
    matching ruleset JSON (or a --print-ruleset flag) next to the scaffold, so
    the context string is generated by the same code that generates the workflow
    and cannot drift from it.
  3. If a result job name ever changes, that is now a breaking change for every
    adopter ruleset. Worth stating as a contract in
    docs/reference/release-system.md.

Context

Found while onboarding componere at v0.1.17. First real publication will
confirm the contexts resolve; the strings are byte-identical to the ones
meigma/homebrew-tap already enforces.

Activity

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

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions