feat(api): recover dropped services (scpm, orchestrator, asset-graph, infrastructure) and fix command-name collisions#150
Conversation
… infrastructure) and fix command-name collisions
The generated command surface is built from the published OpenAPI bundle by
scripts/generate/main.go. Two issues hid large parts of the platform:
1. The generator read a stale, untracked, gitignored copy of the spec at
../public-docs/specs/merged-openapi.yml (dated Apr 28). Repoint it at the
canonical, pipeline-maintained bundle
../public-docs/.gitbook/assets/api/nullify-openapi-bundle.yaml. This alone
recovers */scan-runs (sast/sca/secrets), /context/repo-scans, and
/context/sboms/{generate,resolve}.
2. serviceMapping's prefix allowlist dropped any path whose prefix wasn't
listed, silently discarding /scpm/ (24 ops), /orchestrator/ (9),
/asset-graph/ (4), and /infrastructure/ (7) even though they are in the
bundle. Add those prefixes and wire the new Register*Commands into root.go.
Also fixes generated-command correctness:
- generateCobraUse produced colliding names within a service parent (e.g.
/dast/pentest/scans and /dast/bughunt/scans both -> "list-scans"), silently
shadowing each other in cobra. assignCobraUses now guarantees a unique Use per
service, expanding collisions to include distinguishing path segments
(list-pentest-scans / list-bughunt-scans).
- Path parameters now generate cobra.ExactArgs(n) instead of MaximumNArgs(n), so
a missing required arg fails clearly instead of producing a malformed URL.
Endpoints generated: 458 across 12 services (was 366 across 8).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Make CLI codegen self-contained and reproducible instead of depending on a sibling ../public-docs checkout. - Add `make fetch-spec`: downloads the OpenAPI bundle from the (private) nullify monorepo at a pinned commit (SPEC_REF) via `gh api` (raw media type, so the 2 MB file isn't subject to the contents API's 1 MB base64 limit). Requires GitHub auth. - Vendor the bundle at spec/nullify-openapi-bundle.yaml and generate from it offline. To update: bump SPEC_REF, `make fetch-spec`, `make generate-api`, commit both. - Make generation deterministic: extractEndpoints sorted only by (service, path), but the spec's paths/methods decode from YAML maps with random iteration order, so two operations on one path could swap and produce spurious diffs. Tie-break on method as well. Verified identical across consecutive regens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Followup: made codegen self-contained and reproducible. The generator no longer depends on a sibling |
…ng it /auth/* (access/refresh/github tokens, logout) was absent from both serviceMapping and excludePrefixes, so it fell through the silent-drop path this PR otherwise documents and recovers from. The CLI drives those endpoints through internal/auth, not the generated `nullify api` surface, so make the exclusion explicit and auditable. Generated output unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Review — solid, ship-worthyVerified the 86k-line diff is honest and the generator logic is sound. Independently verified
Addressed (pushed to this branch)The PR documents the silent-drop footgun but Minor, non-blocking
|
The published bundle is the merge of each service's openapi-public.yml, so
genuinely internal endpoints are excluded at the source and never reach the
bundle (verified: zero /internal/ paths). The /internal/ excludePrefix matched
nothing and implied internal paths could leak into the public surface. Remove
it; keep /auth/ and /core/{bitbucket,jira}/ which the bundle does carry.
Generated output unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Why
The generated
nullify api …command surface is built from the published OpenAPI bundle byscripts/generate/main.go. An audit found two silent filters hiding large parts of the platform from the CLI:../public-docs/specs/merged-openapi.yml— an untracked, gitignored, stale (Apr 28) copy. The canonical, pipeline-maintained bundle is../public-docs/.gitbook/assets/api/nullify-openapi-bundle.yaml.serviceMappingdropped any path whose prefix wasn't listed, silently discarding entire services that are in the bundle.What
*/scan-runs(sast/sca/secrets),/context/repo-scans, and/context/sboms/{generate,resolve}./scpm/,/orchestrator/,/asset-graph/,/infrastructure/) toserviceMapping+serviceDescriptions, and wireRegisterScpmCommands/RegisterOrchestratorCommands/RegisterAssetGraphCommands/RegisterInfrastructureCommandsintoroot.go.generateCobraUseproduced colliding names within a service parent (e.g./dast/pentest/scansand/dast/bughunt/scansboth →list-scans), which silently shadow each other in cobra. NewassignCobraUsesguarantees a uniqueUseper service, expanding only the colliding names with distinguishing path segments (list-pentest-scans/list-bughunt-scans).cobra.ExactArgs(n)instead ofMaximumNArgs(n), so a missing arg fails clearly instead of building a malformed URL.Result: 458 endpoints across 12 services (was 366 across 8). SCPM (findings CRUD/triage/allowlist/autofix/retriage/upload), orchestrator (batch autofix, code reviews, retriage, onboarding), asset-graph, and infrastructure graphs are now reachable.
Compatibility
Some auto-generated
nullify api <svc>command names change where they previously collided (and were therefore already broken/shadowed). The renames make every endpoint reachable. Labeledminor.Test plan
make build✅,make unit✅,go vet ./...✅nullify api --helplists the 4 new services;nullify api scpm findings --help;nullify api sastshowslist-scan-runs; missing required arg →Error: accepts 1 arg(s), received 0.🤖 Generated with Claude Code