fix(schema): OpenAPI 3.1 bump + schema fixes#1025
Open
tdabasinskas wants to merge 7 commits into
Open
Conversation
The generated schemas declare JSON Schema draft 2020-12, where OpenAPI 3.0's `nullable` keyword does not exist. The build now consumes GitHub's OpenAPI 3.1 description, which models null-ability as `type: [X, 'null']` unions instead. Update the schema build script to reference the `descriptions-next` directory containing the OpenAPI 3.1 spec. Add comprehensive test coverage to ensure no `nullable` keywords survive in the dereferenced output and verify that required-but-nullable fields use proper type unions.
Contributor
There was a problem hiding this comment.
Pull request overview
Updates schema generation to consume GitHub’s OpenAPI 3.1 (“descriptions-next”) snapshot so nullability is expressed in JSON Schema 2020-12 terms (e.g., type: [..., "null"]) instead of OpenAPI 3.0’s nullable keyword, and regenerates dereferenced artifacts accordingly.
Changes:
- Point
schema/*.jsonexternal$refs atdescriptions-next/(OpenAPI 3.1) instead ofdescriptions/(OpenAPI 3.0). - Update
script/build-schema’s resolver pattern to read fromdescriptions-next. - Add a unit test to ensure generated dereferenced schemas contain no
nullablekeywords and that branch protection fields allownull.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| test/unit/schema.test.js | Adds regression tests to prevent nullable leakage and verify nullability for key branch protection fields. |
| script/build-schema | Updates the resolver matching to align dereferencing with the OpenAPI 3.1 source URLs. |
| schema/settings.json | Switches GitHub spec $refs to descriptions-next URLs. |
| schema/repos.json | Switches GitHub spec $refs to descriptions-next URLs. |
| schema/suborgs.json | Switches GitHub spec $refs to descriptions-next URLs. |
| schema/dereferenced/settings.json | Regenerated dereferenced schema reflecting OpenAPI 3.1 null unions and upstream drift. |
| schema/dereferenced/repos.json | Regenerated dereferenced schema reflecting OpenAPI 3.1 null unions and upstream drift. |
| schema/dereferenced/suborgs.json | Regenerated dereferenced schema reflecting OpenAPI 3.1 null unions and upstream drift. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The schema now uses `anyOf` to allow `protection: null` as a safe-settings semantic for deleting branch protection. Updated tests to: - Navigate the `anyOf` structure to find the object variant - Verify that `protection: null` is allowed in all schemas with branches This ensures tests correctly validate the schema's support for the delete-branch-protection semantic, which differs from the GitHub API's PUT body structure.
The previous regex-based canRead check could potentially match unintended URLs. Replace with exact URL comparison to ensure the resolver only serves the pre-dereferenced spec for the exact URL pinned in schema files. Also expand test coverage to verify enforce_admins nullable handling alongside existing required_status_checks check.
nullable fields validateThe `rulesets` property in both `repos.json` and `suborgs.json` was incorrectly referencing the org-level ruleset API schema. Since these files feed the rulesets plugin at repository scope (via `childPluginsList` in `lib/settings.js`), they must validate against the repo-level API shape instead. This ensures: - Org-only condition targeting (like `repository_name`) does not leak into repo-level validation - Actor types like `User` are properly available at repo scope - Schema validation matches the actual API endpoint being called
Replace `type: null` with `enum: [null, {}, [], false]` to match the actual empty value semantics used by the branches plugin. The plugin's `isEmpty` function treats null, empty objects, empty arrays, and false as deletion signals, but the schema only validated null.
Update test to verify all four empty values are accepted in the schema, aligning validation with runtime behavior.
Add explicit `utf8` encoding parameter to all `fs.readFileSync()` calls in schema tests to ensure consistent text file handling across different platforms and Node.js versions.
Replace exact array equality checks with order-independent assertions using `toHaveLength` and `arrayContaining` matchers. This prevents test brittleness when schema property ordering changes during generation or serialization. Affected assertions: - `ruleset.required` array membership - `protection.anyOf` enum values
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1024.
Three related fixes to the config schemas.
1. Consume GitHub's OpenAPI 3.1 description so nullable API fields validate
The generated schemas declare draft 2020-12 but were built from GitHub's OpenAPI 3.0 description, whose
nullable: truemarkers are meaningless in that dialect. Fields the API documents as null-able rejectednullunder any compliant validator:The 18 spec
$refs inschema/*.jsonnow point atdescriptions-next/, GitHub's OpenAPI 3.1 build of the same pinned snapshot date, and the resolver inscript/build-schemaserves the pre-dereferenced spec for exactly that URL. OpenAPI 3.1 is native JSON Schema 2020-12: null-ability arrives astype: [X, "null"]unions, so the output is dialect-true with no post-processing. (Bonus: the 3.1 spec also addsUserto the ruleset bypassactor_typeenum.)2. Allow
protection: null, safe-settings' own delete-branch-protection syntaxThe branches plugin deletes branch protection when the config says
protection: null(isEmptyinlib/plugins/branches.js; there's an upstream unit test for exactly this shape), but the schema modeledprotectionas the API's PUT body only, which istype: object. The property is now ananyOfof{ "type": "null" }and the existing PUT-body reference, in all three schemas. Object values still validate against the full PUT body.3. Validate rulesets in suborg and repo configs
Suborg and repo override files can configure
rulesets:(they feed the rulesets plugin at repo scope viachildPluginsList), butschema/repos.jsonandschema/suborgs.jsonhad norulesetsproperty, so anything under that key passed validation silently:Both files actually already contained an unused
RulesetSettingsdefinition; it's now wired to arulesetsproperty, and pointed at the repo-levelPOST /repos/{owner}/{repo}/rulesetsbody instead of the org-level one it referenced (repo rulesets have norepository_name/repository_propertycondition targeting). The config above now fails with three precise errors; realistic per-repo ruleset configs validate.Not included:
actor_type: EnterpriseOwner, which the API returns for enterprise-owned orgs but which is missing from GitHub's own OpenAPI spec (reported at github/rest-api-description#); it will flow in with a future spec pin bump.Regenerated artifacts
schema/dereferenced/*.jsonare rebuilt withnpm run build:schemaonly — no hand edits. Notes for reviewing the diff: rebuilding at current HEAD without any changes already produces a ~264-line diff (the committed artifacts were stale); theanyOfwrapper re-indents the whole protection subtree (git diff -wshows the real change is +72 lines); the rulesets addition inlines the repo-ruleset subschema. Regeneration is deterministic: running the build twice produces no further changes.Tests
New
test/unit/schema.test.js: nonullablekeyword survives anywhere;required_status_checks,enforce_admins, andrestrictionsaccept null;protection: nullis accepted by all three schemas; suborg/repo rulesets validate against the repo-level shape (requiredname+enforcement, no org-only conditions,Userin the bypass enum).