feat(cli): v0.2 CRUD surface — registry-driven create/update/delete for 14 resource types + apply -f - #1
Merged
Merged
Conversation
added 15 commits
August 5, 2026 21:40
TestFactoryBuildsCrudAndRoutesFlags previously only checked cmd.Use and subcommand names, never running a leaf's RunE. Extend it to actually execute `create --name widget-x` and assert on the body createFn received and the rendered output, and add a required-field-missing case asserting exit code 2.
- bodyFromFlagsAndFile replaces Task 1's bodyFromFlags: loads an optional -f YAML/JSON document via sigs.k8s.io/yaml (converts to JSON first so ints/bools match the SDK models), then overlays any changed flags on top (flags win); required-field enforcement now also accepts a value supplied only via -f. - newCreateCmd/newUpdateCmd gain a --file/-f string flag and call the new helper. - newApplyCmd (internal/cli/apply.go) reads -f, splits the file into YAML documents on a lone "---" line, and for each document reads kind, looks up registry[kind] (clierr.Config on unknown kind), and dispatches to that resource's updateFn (when the doc carries an id) or createFn otherwise; org is resolved per the resource's OrgMode via the existing resolveResourceOrg helper. Registered on root. Tested against synthetic in-memory resources registered/deregistered inside apply_test.go (real check/notification-channel resources land in Task 3), covering multi-doc dispatch, create-vs-update routing, unknown kind, missing -f, and the flag-overrides-file merge.
…CRUD Migrate the four existing typed resources onto the Task 1/2 resource registry (list/get/create/update/delete via newResourceCmd), adding full create/update/delete for all of them without regressing v0.1 behavior: org keeps its slug-aware get, check keeps its name-aware get plus run/pause/resume/test-alert, notification keeps test. Adds mapToBody[T] (the inverse of toMap) so create/update bodies are built straight from the SDK's Create models instead of hand field mapping.
DeleteOrganizationParams.Confirm must equal the organization's name, but deleteFn always sent it empty, so a real backend rejected every org delete. Fetch the org first to read its name and pass it as confirm — the CLI's own --yes gate already got the user's consent.
Register team and service as OrgArg registry resources (Teams.*/ Services.* SDK calls), wired directly into root.go via newResourceCmd(registry["team"|"service"]).
Resource gains an optional ListCols: list renders it when set, else falls back to Cols (unchanged for every other resource). service's list endpoint (ServiceListItemResponse) returns health_status/checks_count that its get/create/update responses (ServiceDetailResponse/ServiceResponse) don't have, so those three verbs were rendering blank cells for them.
Register maintenance-window (Org=OrgParam, org rides ListMaintenanceWindowsParams.OrganizationId) and playbook (Org=OrgArg, org is a required path arg) on the resource registry, wired into root.go alongside team/service/oncall-schedule/escalation-policy.
lifecycle-watch update discarded its id arg and, since required-field enforcement only applies on create, could silently upsert an empty-keyed watch. Remove updateFn (list/get/create/delete only), promote vendor/resource-type/resource-id to Required on create, and document the upsert semantics on newLifecycleWatchCmd's create Long text.
Incidents are addressed by the (check_id, log_id) pair the API uses, not a single id, and agents are created by self-registration against a minted token rather than a create call — neither fits the generic CRUD verb factory, so both are hand-written cobra leaves added onto the existing incident/agent parent commands, same pattern as check's run/pause/resume.
output.Render's --quiet path indexed t.Cols[0] unconditionally, which
panicked for any zero-column table — exactly what renderRawObject produces
for a bare {} ack/resolve confirmation. Guard it at the root, and have
renderRawObject print a short confirmation instead of a columnless table in
the default format.
incident acknowledge also decided whether to send --note by comparing it
against "", which conflated an omitted flag with an explicit empty one; use
cmd.Flags().Changed("note") instead, matching bodyFromFlagsAndFile's
established pattern.
Add apply -f - stdin support (get -o yaml | apply -f -) and a round-trip test proving get -o yaml output feeds back into apply as the same update body, for check (bespoke get, OrgParam) and team (generic get, OrgArg). Update README with the full v0.2 CRUD surface: generic list/get/create/ update/delete per resource, apply -f, hybrid flags+-f input, and the incident/agent actions.
Member
Author
|
Live E2E (throwaway org, org-scoped PAT, cleaned up) confirmed the CRUD surface really works — check/team full lifecycle (create→get→update-verified→delete→404), apply -f multi-doc, org-scope isolation, client-side Required enforcement, clean error surfacing. Two runtime findings for the v0.2.x backlog (not caught by code review, only by real E2E):
|
createFn ignored the resolved orgID (bound to _), so `notification create --org X` always posted without organization_id and the API created a personal channel instead of an org-scoped one. Thread orgID through and set body["organization_id"] when unset, letting an explicit -f/--organization-id value still take precedence.
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.
Phase 1 of the full-coverage CLI (v0.2 → v0.3 generate → v0.4 export). Spec/plan in
docs/superpowers/.Adds a registry-driven imperative surface on
syschecks-gov0.3.3. A singleResource(SDK method closures + field descriptors + serialization) is registered per type; a generic factory turns each intolist/get/create/update/delete(a verb's subcommand exists only where the SDK supports it). Three features share the registry so they can't diverge (CRUD now; export/generate in later phases).Surface
apply -f <file|->— multi-doc, dispatch bykind, update-if-id-else-create; reads stdin on-.-f file.yaml(flag overrides file);get -o yamlround-trips throughapply.incident get/acknowledge/resolve,agent token/delete. v0.1 (check run/pause/resume,probe,verify,auth,config) unchanged.Quality
Built task-by-task with TDD (15 commits), a per-task spec+quality review and a final whole-branch review — all clean. Full
go build/vet/test ./...green; v0.1 behaviour not regressed.Known backlog (non-blocking, filed for v0.2.x)
team update --slug(and some contact-method fields) are silently dropped when the SDK Update model lacks them — warn or stop advertising those flags.get -o yamlemits nokind, so a literalget -o yaml | apply -f -needs a manualkind:(emit it from get).apply -fskips client-sideRequiredvalidation (fails server-side instead).check create --helpexposes ~87 flags (wholeCheckCreatemodel) — curate a per-check-type common subset.integration-keylist wrapper key/columns are unverified guesses (backend schema is empty) — confirm against a live response.