feat: Add a check-schema-fields command to the metadata tool - #4375
feat: Add a check-schema-fields command to the metadata tool#4375JamBalaya56562 wants to merge 7 commits into
check-schema-fields command to the metadata tool#4375Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #4375 +/- ##
=======================================
Coverage 98.52% 98.52%
=======================================
Files 195 195
Lines 17745 17745
=======================================
Hits 17484 17484
Misses 261 261 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
gmlewis
left a comment
There was a problem hiding this comment.
Thank you, @JamBalaya56562!
Overall, this is looking good, but there are many small helper functions in schema_fields.go that have no unit tests that I think could greatly benefit from some idiomatic Go table-driven unit tests to document their behavior.
Also, I would really like to see an example of the output of this tool (for example, when some of the exceptions are removed).
Finally, it would be nice if we didn't have to modify the code and that its exceptions could be updated by loading them from the config files like we do in the other tools.
- Load field-optionality exceptions from schema_field_exceptions.yaml instead of hardcoding them in Go, with a --exceptions flag. - Add table-driven unit tests for the schema_fields.go helpers and the new exceptions loader. - Document the hasEnoughSharedFields thresholds as named constants. - Use %v instead of %s in the check-schema-fields verbose output. - Note the intentional Go-initialisms duplication with tools/structfield.
|
Thanks for the review, @gmlewis! I've addressed all three points in c6425df.
$ script/metadata.sh check-schema-fields --exceptions empty.yaml --verbose
Found 7 schema field issues
Checked 4 OpenAPI schema/Go struct pairs; skipped 1138 OpenAPI schemas
github/dependency_graph_snapshots.go:83: DependencyGraphSnapshot.Detector (detector from snapshot): field is required and non-nullable in the OpenAPI schema but is a pointer [descriptions/api.github.com/api.github.com.json]
github/dependency_graph_snapshots.go:82: DependencyGraphSnapshot.Job (job from snapshot): field is required and non-nullable in the OpenAPI schema but is a pointer [descriptions/api.github.com/api.github.com.json]
github/dependency_graph_snapshots.go:81: DependencyGraphSnapshot.Ref (ref from snapshot): field is required and non-nullable in the OpenAPI schema but is a pointer [descriptions/api.github.com/api.github.com.json]
github/dependency_graph_snapshots.go:84: DependencyGraphSnapshot.Scanned (scanned from snapshot): field is required and non-nullable in the OpenAPI schema but is a pointer [descriptions/api.github.com/api.github.com.json]
github/dependency_graph_snapshots.go:80: DependencyGraphSnapshot.Sha (sha from snapshot): field is required and non-nullable in the OpenAPI schema but is a pointer [descriptions/api.github.com/api.github.com.json]
github/repos_deployment_branch_policies.go:29: DeploymentBranchPolicyRequest.Name (name from deployment-branch-policy-name-pattern-with-type): field is required and non-nullable in the OpenAPI schema but is a pointer [descriptions/api.github.com/api.github.com.json]
github/actions_workflow_runs.go:138: ReviewCustomDeploymentProtectionRuleRequest.Comment (comment from review-custom-gates-state-required): field is optional in the OpenAPI schema but is not a pointer, slice, map, interface, or selector type [descriptions/api.github.com/api.github.com.json](The three |
gmlewis
left a comment
There was a problem hiding this comment.
Thank you, @JamBalaya56562 - please fix the new lint errors caused by the new unit tests, then we should be able to proceed with this PR.
Use %v instead of %d/%s to satisfy the fmtpercentv linter, and replace a no-argument t.Errorf with t.Error for the revive unnecessary-format check.
Thanks again, fixed. |
gmlewis
left a comment
There was a problem hiding this comment.
Thank you, @JamBalaya56562!
LGTM.
Awaiting second LGTM+Approval from any other contributor to this repo before merging.
stevehipwell
left a comment
There was a problem hiding this comment.
@JamBalaya56562 I like the intent here but I'm not sure I'm onboard with the approach you've taken. Could you provide a list of the structs that are mapped and a list of the structs that are skipped? Ideally it'd be good to see how the mapped structs were mapped and if by name if the fields would have resulted in a match too?
What I'm currently thinking is that this linking is brittle and has the potential to cause churn for no real reason. IMHO it'd make more sense to use a //meta:schema comment on the struct to control the mapping and ignores as this would place the logic next to the code it's related to as well as making it deterministic.
|
@stevehipwell thanks for the thorough review — I ran the numbers and I think you're right. Here is the full mapped/skipped breakdown you asked for ( Mapped (only 4 pairs, all via To your question about name-vs-field cross-checking: nothing matched by name, so there is nothing to corroborate — the CI check relies entirely on exact field-set equality. Skipped (1138):
And the brittleness you flagged is concrete in that last bucket, e.g.: An unrelated struct coincidentally matching rule-params schemas purely on So I agree with your read: the exact-field-set heuristic gives very low recall (4 request structs) and real false positives — exactly the "brittle + churn" failure mode you described. The field-comparison engine itself is sound (it did surface genuine required-vs-pointer mismatches on the structs it reached), so I'd like to keep that and replace only the matching: move to an explicit I'll rework the PR along these lines. Since this started from @gmlewis's proposal in #4319, I want to make sure the annotation format works for all of you before I invest in the rewrite — happy to adjust the exact syntax. |
Address inline review feedback on the check-schema-fields command:
- Replace the sliceSet helper with []string plus slices.Contains for the
exceptions, schema-name filter, and required-field sets.
- Replace append([]string{}, s...) with slices.Clone(s).
- Drop the --exceptions flag and always read the fixed
tools/metadata/schema_field_exceptions.yaml path.
- Drop the unused --json flag from check-schema-fields.
- Use githubClient (which requires GITHUB_TOKEN) instead of a duplicate
publicGithubClient.
- Remove the trivial defaultJSONName pass-through.
- Wrap the added comments to the package's line-length convention and trim
the exceptions file header.
|
@alexandear thanks for the review — I pushed all nine inline fixes in On your main concern — only 4 schema/struct pairs get checked — you and @stevehipwell are right, and that's the real limitation rather than the nits. The exact-field-set matching is inherently low-recall: most go-github request structs don't have a JSON field set identical to their OpenAPI schema, so the heuristic simply can't link them. I'd like to replace the matching with an explicit Since this started from @gmlewis's proposal in #4319, I'd like to confirm the annotation format with the three of you before reworking it — happy to hear preferences. |
|
@gmlewis Replying on the redesign direction (sorry for the wait). Following the coverage concern from @stevehipwell and @alexandear — 4/1142 schemas matched, with accidental-match risk — I'd like to replace the field-set matching entirely with explicit // CreateCommitCommentRequest represents a request to create a commit comment.
//
//meta:schema request POST /repos/{owner}/{repo}/commits/{commit_sha}/comments
type CreateCommitCommentRequest struct { ... }
If this direction works for you I'll rework this PR accordingly — or split the redesign into a fresh PR if you'd prefer this one closed. Either is fine on my side. |
That sounds like a good plan to me, and continuing in this PR is fine with me too. No rush. |
Replace the fuzzy schema-to-struct matching (name heuristics, exact field-set matching, ambiguity dropping, and match thresholds) with explicit opt-in annotations: a struct doc comment carries one "//meta:schema <request|response> <METHOD> <path>" line per operation whose body schema it must match, mirroring the //meta:operation convention. Only annotated structs are checked, annotations that do not resolve to an operation in the OpenAPI descriptions are reported as issues, and the field-level comparison is unchanged. The magic match thresholds and the duplicated initialisms list are removed along with the matcher, and the stale exception entries are dropped now that unannotated structs are simply not checked.
Annotate 25 dedicated request body types (26 annotations) so that check-schema-fields validates them against the OpenAPI descriptions. All annotations verify clean against the current descriptions: "Found 0 schema field issues; Checked 26 annotations on 25 annotated structs". Further annotations can land incrementally alongside future pass-by-value conversions for google#3644.
|
@gmlewis Done — the rework is pushed:
The PR description is updated to match. Local runs of the tools tests, the full |
gmlewis
left a comment
There was a problem hiding this comment.
Ah, very nice, @JamBalaya56562!
My only concern is that when anyone makes a new PR that adds a struct, how will they know to opt-in?
Is there any way for the linter to give some kind of suggestion like "The following structs are missing the proposed metadata:" or something like that?
Or do we need a new tool that can fill in ALL missing struct metadata? Thoughts?
cc: @stevehipwell - @alexandear
|
@JamBalaya56562 Since all API methods already have |
|
Good question — that was effectively where the first iteration of this PR headed (it derived request structs from That said, signature-derived pairs could be a nice follow-up as a suggestion mode — e.g. listing unannotated candidates, or auto-inserting |


Adds a
check-schema-fieldscommand totools/metadatathat checks Go struct JSON field optionality against GitHub's OpenAPI schemas — required, non-nullable schema fields must be non-pointer fields without an omit option, optional fields must remain omittable, and the field sets must line up. This grew out of @gmlewis's suggestion in #4319 and helps the pass-by-value work in #3644, where each conversion needs exactly this comparison done by hand today.How structs are matched to schemas (reworked)
The original submission tried to match OpenAPI component schemas to structs automatically and could only do so unambiguously for 4 of 1142 schemas, as @stevehipwell and @alexandear pointed out. That matching is now gone. Instead, a struct opts in with explicit
//meta:schemaannotations in its doc comment, mirroring the existing//meta:operationconvention:request/responseselects the operation's request body or first 2xx JSON response schema; the operation is resolved against the downloaded descriptions (api.github.com, then ghec, then ghes — so GHES-only operations resolve too) using the same path normalization as//meta:operation.//meta:operation.Struct.Fieldexceptions file remains for annotated structs with a deliberate deviation, but its stale entries are dropped — under opt-in, a struct with known deviations simply isn't annotated until fixed.Starter set
25 request types (26 annotations) are annotated in this PR — the dedicated request bodies already converted under #3644 (issue/gist/PR comments, milestones, keys, releases, merges, deployment branch policies, autolinks, custom repo roles, external groups, PR reviews, template repos). Against the current descriptions:
Future conversions can add their annotations incrementally, so coverage grows with #3644 instead of depending on a heuristic.
CI
check-schema-fieldsruns in the existingcheck-openapijob (it needs the downloaded descriptions and aGITHUB_TOKEN), alongsideupdate-openapi --validate. The command exits non-zero on any issue.Ref #4319.