Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/upgrading/v2-to-v3.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ order: 20

# Upgrading from v2 to v3

Sixty-one breaks. Eleven are for library consumers (sections 9, 23, 24, 27, 34, 35, 54, 56, 58, 60, and 61), and two are for anyone pinning a sha or installing `@next` (sections 21 and 59).
Sixty-one breaks. Twenty-six are for library consumers (sections 9, 22 to 29, 34, 35, 37 to 41, 45, 47, 49, 51, 54 to 56, 58, 60, and 61), ten of those also refusals below, and two are for anyone pinning a sha or installing `@next` (sections 21 and 59).
Comment thread
Vivswan marked this conversation as resolved.

Eighteen are parse-time refusals (sections 36 to 52 and 55): a declaration GitHub would reject, or that could never converge, now fails before any request. Sections 53 and 57 are silent: YAML merge keys resolve, and one validation message is respelled.

Expand Down
2 changes: 2 additions & 0 deletions docs/upgrading/v2-to-v3/layering.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ Every list section folds this way, by the key its planner matches on: labels, co

The value `merge` is gone. `labels: {_layering: merge, entries: [...]}` fails with `labels._layering must be one of "replace", "shallow", "deep"; got a string that is none of them`, and the `layering` input refuses it too, naming the same three values.

For `@vivswan/github-settings-as-code` consumers, the `Layering` type is `"replace" | "shallow" | "deep"`, so a `layering: "merge"` in `MergeOptions` stops compiling; write `"deep"`, the fold it meant.

Fix: write `deep` where a layer said `merge`, and `_layering: replace` on any list section a higher layer meant to replace whole. Under `shallow` and `deep` an empty higher list adds nothing; clearing a list takes `replace` with an empty list. The [layering guide](../../operate/layering.md#four-knobs) owns the rules.

## 29. mode: merge is mode: render
Expand Down
10 changes: 9 additions & 1 deletion docs/upgrading/v2-to-v3/refusals-lists.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ v3 branches[0].protection.required_status_checks.enforcement_level: ... is Git
| A scalar where `required_status_checks` or `required_pull_request_reviews` goes | Refused on a wildcard entry, passed through to the PUT on a literal one | Refused: a mapping or `null` on every entry |
| An actor object copied from the GET (`restrictions.users: [{login: octocat}]`) | Sent to the PUT, which 422ed | Refused: write the `login` or `slug` string, `octocat` |

For `@vivswan/github-settings-as-code` consumers, `required_status_checks` and `restrictions` under `SectionInput<"branches">` (and so `SettingsFile`) are typed shapes, once `unknown`; a check list without `strict`, or an actor object, stops compiling.

Fix: declare the PUT's shape (bare booleans, `strict` beside the check list, actors as their login or slug strings) instead of pasting the GET. Also new: a live `restrictions` holder whose `users`, `teams`, and `apps` are all empty is a push restriction that lets nobody through, so a file that omits it now sees the omitted-live drift line and a loud PUT instead of a silent lift.

## 42. Collaborators and teams: permissions and slugs
Expand Down Expand Up @@ -121,6 +123,8 @@ v3 webhooks[0].config.url: "hooks.example.com/ci" is not an absolute URL (the
webhooks[0].events[1]: "pushes" is not an event GitHub delivers to repository webhooks ("*" means every event); the accepted names are GitHub's list at https://docs.github.com/webhooks/webhook-events-and-payloads, read from @octokit/openapi-webhooks, so an event GitHub added since arrives in the release that bumps that package
```

For `@vivswan/github-settings-as-code` consumers, under `SectionInput<"webhooks">` (and so `SettingsFile`) `content_type`, `insecure_ssl`, and each `events` item are the unions above, once `string` or `number`; `JSON`, `2`, or `pushes` stops compiling.

Fix: an absolute URL, `json` or `form`, `"0"` or `"1"`, and event names from GitHub's repository list. The list is pinned to `@octokit/openapi-webhooks`, so an event GitHub adds later is refused until the release that bumps that package.

## 48. Secret scanning patterns must compile
Expand Down Expand Up @@ -160,6 +164,8 @@ v3 rulesets[0].enforcement: Invalid option: expected one of "active"|"evaluate

Bypass actors are typed from the spec: Integration, RepositoryRole, Team, and User need an `actor_id`; DeployKey takes none and never `pull_request`; `pull_request` applies to branch rulesets only. The 23 known rule types carry their parameters typed from the spec, in GitHub's casing; an unknown rule type still passes through, so a type GitHub ships tomorrow works the day it ships.

For `@vivswan/github-settings-as-code` consumers, `enforcement` and `actor_type` under `SectionInput<"rulesets">` (and so `SettingsFile`) are the enums above, once `string` and `unknown`; `enabled` stops compiling; a rule's parameters still compile.

Fix: spell the enums as GitHub does, give each actor its id, and write `~ALL` or `~DEFAULT_BRANCH`.

## 50. Milestones: due_on is a day
Expand Down Expand Up @@ -205,7 +211,7 @@ GitHub answers `protection_rules: []` for an unprotected environment, and the fl
| More than 6 `reviewers` | 422 | Refused: GitHub's cap |
| `deployment_branch_policies[].type: wildcard` | Deleted the live policy, then the create 422ed; every run retried | Refused: `branch` or `tag` |

Comment thread
Vivswan marked this conversation as resolved.
For library consumers, `DeploymentBranchPolicyConfig.type` narrows from `string` to `"branch" | "tag"`.
For `@vivswan/github-settings-as-code` consumers, a policy's `type` under `SectionInput<"environments">` (and so `SettingsFile`) is `"branch" | "tag"`, where the pre-release v3 builds typed `string`; a `string` there no longer compiles.

## 52. Autolinks: charset, placeholder, and overlapping prefixes

Expand Down Expand Up @@ -249,6 +255,8 @@ v3 branches[0].protection.restrictions.users: protection.restrictions must car

GitHub's protection PUT requires `users` and `teams` under `restrictions` and takes `apps` as optional, so each missing list is refused before any request. The two review-side holders are unchanged: `dismissal_restrictions: {}` and `bypass_pull_request_allowances: {}` stay legal, since GitHub documents the empty mapping there as "disabled".

For `@vivswan/github-settings-as-code` consumers, `restrictions` under `SectionInput<"branches">` (and so `SettingsFile`) requires `users` and `teams`, where the pre-release v3 builds typed the block `unknown`; `restrictions: {}` stops compiling.

Fix: declare both lists (`users: []` and `teams: []` when none), or write `restrictions: null` to lift the push restriction.

## 57. A closed section's unrecognized key names the entry by index
Expand Down
10 changes: 10 additions & 0 deletions docs/upgrading/v2-to-v3/refusals-repository.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ v3 repository.has_downloads: has_downloads is reported by GitHub but cannot be
| A topic outside `^[a-z0-9][a-z0-9-]{0,49}$` after lowercasing, or more than 20 | 422 from the topics PUT | Refused naming the topic |
| An empty topic (`topics: [""]`, `topics: ""`, `ci,,tooling`) | Dropped silently, so `[""]` cleared every topic | Refused by index |

For `@vivswan/github-settings-as-code` consumers, the commit-message keys under `SectionInput<"repository">` (and so `SettingsFile`) are enums and `security_and_analysis` is a closed shape, once `unknown`; a `string` or an unknown sub-key stops compiling.

Fix: delete the GET-only key, declare the title beside the message, and spell topics in GitHub's alphabet. A key in neither the GET nor the PATCH still passes through, so a field GitHub adds later works on day one.

## 38. Pages: GET-only fields and the source path
Expand All @@ -45,6 +47,8 @@ v3 pages.custom_404: GitHub reports this field on the Pages site and the updat

`url`, `html_url`, `status`, `custom_404`, `protected_domain_state`, `pending_domain_unverified_at`, and `https_certificate` are refused. `public: false` stays declarable: Enterprise Cloud shares the same host, so the check run's note beside the drift says why it cannot converge elsewhere.

For `@vivswan/github-settings-as-code` consumers, `source.path` under `SectionInput<"pages">` (and so `SettingsFile`) is `"/" | "/docs"`, where the pre-release v3 builds typed `string`; another path there stops compiling.

Fix: delete the reported field from the file, and publish from `/` or `/docs`.

## 39. Actions: fields that could never converge
Expand All @@ -69,6 +73,8 @@ v3 actions.artifact_and_log_retention.maximum_allowed_days: maximum_allowed_da
| An `approval_policy` outside GitHub's three; a malformed or repeated OIDC claim key; `include_claim_keys` beside `use_default: true` | Refused naming the key |
| A fractional or non-positive retention or cache limit | Refused: positive integers |

For `@vivswan/github-settings-as-code` consumers, in `SectionInput<"actions">` (and so `SettingsFile`) `approval_policy` is an enum, once `string`, and `selected_actions` is closed, once an open mapping; a `string` or an unknown key there stops compiling.

Fix: delete the reported-only field, fix the `selected_actions` spelling, and write booleans and integers unquoted. `fork_pr_workflows_private_repos` now requires only `run_workflows_from_fork_pull_requests`, as the request body does.

## 40. Setup sections: GET-only keys, languages, and the runner pair
Expand All @@ -89,6 +95,8 @@ v3 code_scanning_default_setup.schedule: "schedule" is reported by GitHub but
code_quality_setup.runner_label: runner_label "gpu" is declared under runner_type: "standard", where GitHub ignores it; set runner_type: "labeled", or remove runner_label
```

For `@vivswan/github-settings-as-code` consumers, `languages` under `SectionInput<"code_scanning_default_setup" | "code_quality_setup">` (and so `SettingsFile`) is an array whose items are the PATCH's enum, once `string`; `javascript` stops compiling.

Fix: drop `schedule` and `updated_at`, spell languages as the PATCH takes them (`javascript-typescript`; code quality's GET-only `rust` has no declarable name), and pair a string `runner_label` with `runner_type: labeled`. A declared key outside the slice that the GET never echoes now earns the never-converges note in check and apply.

## 44. Check-suite preferences: app_id
Expand Down Expand Up @@ -122,4 +130,6 @@ v3 interaction_limits.limit: limit is one of existing_users, contributors_only
interaction_limits: Unrecognized key: "expires_at"; interaction_limits takes limit, expiry, pull_request_creation_cap, and pull_request_creation_bypass (origin and expires_at are what GitHub reports, not what it accepts); remove the key, or fix its spelling
```

For `@vivswan/github-settings-as-code` consumers, `limit` and `expiry` under `SectionInput<"interaction_limits">` (and so `SettingsFile`) are the enums above, where the pre-release v3 builds typed `string`; another value there stops compiling.

Fix: spell the enum values as listed, keep the cap in range, and delete `origin` and `expires_at`, which GitHub reports but never accepts.
4 changes: 4 additions & 0 deletions docs/upgrading/v2-to-v3/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ The action and the command line print the same line for the same problem; the re
| `gsac init --settings-file a,b` | `the --settings-file value "a,b" contains a comma or a newline, which check and apply read as a list separator ...` | | `the "settings-file" input is "a,b", which contains a list separator: init writes exactly one settings file ... Name one file` |
| An unreadable `repos-dir` file | `cannot read settings from <path>: ... Fix the file, or delete it to stop managing this repository` | | `cannot read the central settings file <path>: ... Fix the file, or delete it to stop managing this repository` (a YAML syntax error in it reads the same way, where v2 said `cannot parse`) |

For `@vivswan/github-settings-as-code` consumers: `parseConfig` takes a third argument, the face's `RunCapabilities`, and refuses `private-report: artifact` there (`input-artifact-unsupported`); a two-argument call stops compiling.

## 25. Every live read is parsed at the port

Every GET and GraphQL query a section issues now passes through one parser before the section sees the body, so a response off the documented shape fails the section instead of flowing into a comparison.
Expand All @@ -136,6 +138,8 @@ Two smaller moves ride along:
- A section that reads anything must declare `snapshot()`; only the write-only `check_suite_preferences` reports `unsupported`, and the `snapshot is not implemented for this section yet` note is gone.
- The organization-only sections (`teams`, `custom_properties`) are probed for the owner kind by the registry, ahead of their own plan and snapshot. The personal-account note is unchanged. A settings-file mistake in those sections (two entries naming one team) is validation's, before any request ([section 34](#34-file-only-checks-run-before-the-first-write)).

For `@vivswan/github-settings-as-code` consumers, a `SectionModule` typed over its literal endpoint dictionaries requires `snapshot()` when an endpoint declares a read; with the erased default, which `sectionModule()` returns, nothing is checked.

## 26. The snapshot file is canonical and undated

The file `mode: snapshot`, `gsac init`, and `snapshotRepository` write carries no timestamp, and its document renders in the one canonical order the rendered file shares, so a snapshot of an unchanged repository is byte for byte the last one and a diff between two snapshots shows only what changed on GitHub.
Expand Down
Loading