Skip to content

feat(go): the remaining platform API areas as commands - #60

Merged
achoimet merged 30 commits into
mainfrom
feat/go-api-parity
Sep 25, 2026
Merged

achoimet merged 30 commits into
mainfrom
feat/go-api-parity

Conversation

@achoimet

Copy link
Copy Markdown
Member

Adds the remaining platform API areas (group 1 of the ticket) as commands, one domain package per area under internal/<area>, wired in internal/cli/<area>.go. Where the API is versioned, only the latest version is used (access tokens: /api/access-tokens/v2 only).

Commands

Group Commands Endpoints
Templates template apply -f … [-R], template delete -i, template import --hub --template … [--overwrite] POST/DELETE /api/experiments/templates…, POST /api/experiments/templates/imports
Environments environment list|get|apply|delete, environment variable get|set [--replace] /api/environments…; variables: PUT merges, POST replaces
Teams team list|get|apply|delete, team member list|add|remove|set, team environment list|add|remove|set /api/teams…
Users user invite --email … [--role] [--team] POST /api/users/invite
Access tokens access-token list|create|recreate|delete /api/access-tokens/v2…
Integrations integration webhook|slack|preflight|preflight-action list|get|apply|delete /api/integrations/…
Reports report users|teams|environments|experiments-executed|experiments-created|services-distribution|services-by-category|services-average POST /api/reports/…
Audit log audit-log [--from] [--to] [-t] GET /api/audit-log
Kill switch killswitch status|activate|deactivate GET/POST/DELETE /api/killswitch
Targets, actions target query, target attribute keys|values, action list|get /api/targets…, /api/actions…
Hubs hub list|get|apply|delete|resync /api/hubs…
Properties property definition list|get|apply|delete, property association list|get|apply|delete /api/properties/…

Conventions follow the existing commands: files pass through as order-preserving documents, get strips what the platform does not take back (version, created*, edited*, hub sync state, team member details, ...) so that get → apply round-trips, apply goes through resource.ApplyFiles and writes a new id back first (not for teams and property definitions, which are named by their key), listings are tables with No … found., paged endpoints are followed to the end. template get now strips the same read-only fields as service get, since templates can be applied now.

Destructive commands ask on a terminal unless --yes is given (like experiment run): every new delete, killswitch activate, access-token recreate, team member set|remove, team environment set. New shared helpers in internal/resource: Confirmed, Body, Optional, UUID, Time, Variables (the last one extracted from service variable set, now shared with environment variable set).

Testing

Every group has command-level tests against platformtest asserting method, path, query, body and output: 52 new test functions, 263 tests and subtests pass in total. gofmt -l, go vet ./..., go test ./... are clean after every commit.

Live on the dev platform (tenant demo, ADM team token), only with resources named cli-e2e-*, all deleted again and checked gone:

Live-tested How
template apply/get/delete created cli-e2e-template, get → edit → apply, deleted
environment *, environment variable * created cli-e2e-environment, round trip, merge and --replace, deleted
team *, team environment *, team member * created team CLIE2E; environments added/set/removed; members add/set/remove only with cli-e2e@example.invalid (unknown, so skipped; --validate refuses it); deleted
access-token list/create/recreate/delete cli-e2e-token for team CLIE2E, expiring in 2 days; recreated, then deleted
integration webhook * cli-e2e-webhook pointing at https://example.invalid/cli-e2e, deleted
property definition * cli-e2e-property, created, updated, deleted
read-only report * (all eight), killswitch status, target query/attribute, action list/get, hub list/get, integration slack/preflight/preflight-action list/get, property association list, audit-log

Unit tests only, and why:

  • killswitch activate/deactivate: activating stops every experiment of the tenant.
  • user invite: sends emails to real people.
  • template import, hub apply/delete/resync: sync external repositories.
  • property association apply/delete: the ADM token gets 403 (Missing permissions to create property associations), which needs an admin token.
  • integration slack/preflight/preflight-action apply/delete: only a webhook integration was allowed to be created live; the code is shared by the four kinds.
  • audit-log returned no entries for the team token (200 with []); a 9-month range timed out on dev. Table and JSON rendering are unit-tested.

Awkward spots in the spec and the platform

  • Integration secrets: GET returns the secret masked ('****'), POST rejects the mask (422), and leaving secret out removes it. So a secret cannot round-trip; integration * apply refuses a file still holding the mask and says to put the secret in or remove it.
  • Team delete: purgeIncludingExperiments=false gets 400 with no body, even for a team without experiments. team delete explains that --purge-experiments is needed.
  • GET /api/environments/{id}/variables is declared as returning a string; it returns the map of variables.
  • GET /api/actions pages under actions instead of items, so it cannot use platform.AllPages; GET /api/properties/associations takes only a page number (no size), while definitions take a PageRequestAO.
  • Reports filter by team, environment and service ids; the CLI takes team keys and environment names and resolves them. Rollup MONTHLY buckets start at from, not at the month start.
  • Access token ids are short strings (e.g. K7DcJbgf), not UUIDs; access-token create has a --type (token type), so its output format flag is -t, --output.
  • template import without templateIds is not described in the spec; the CLI requires --template.
  • Writing a new id back re-renders a YAML file (quotes may change), as schedule apply and service apply already do; only experiment files keep their formatting.

…w-steps

Interrupting `run --wait` (Ctrl-C, or a CI runner's SIGTERM) now cancels
the run it started before exiting with 130 or 143, so an aborted pipeline
no longer leaves an attack running; --keep-running-on-interrupt keeps the
previous behaviour. --timeout cancels a run that takes too long.
--report writes JUnit (or JSON) with a test case per step, and a
Markdown summary goes to $GITHUB_STEP_SUMMARY in GitHub Actions.
Listings print the platform's items, in its order, with -t json or yaml
instead of a table. --jq filters whatever JSON a command prints, strings
raw and values as JSON like gh --jq; jq itself is not needed.
`uses: steadybit/cli@v5` installs the release binary for the runner,
checked against checksums.txt. Released versions are verified on Linux,
macOS and Windows runners after publishing. The README shows the action,
a GitLab job with a JUnit report, and -t/--jq.
…es and profiles

`<kind> diff -f files` prints a unified diff between each file and the
platform and exits with 2 when they differ, so a pipeline can detect
drift. Fields the platform fills in with defaults, and the key or id of a
file matched by externalId or name, are not reported as differences.
`apply --dry-run` reports what an apply would create or update.
`export --team X -d dir` writes a team's experiments, schedules and
services, and the custom profiles they use, one kind per directory.
`apply -d dir` applies them in dependency order (with --dry-run), and
`diff -d dir` reports drift for all of them. Exporting a team from dev
and diffing it straight back reports no differences for 107 files.
--profile <name> uses a configured profile for one command. Shell
completion now offers experiment keys (team first, then its experiments),
team keys, and template, schedule, service and profile ids with their
names; without access it offers nothing and prints nothing.
`execution watch -i ID | -k KEY` follows a run live, redrawn in place on a
terminal and a line per change otherwise; it never cancels the run.
`experiment init` creates an experiment from a template by asking for its
placeholders, team and environment, and writes it to a file.

`experiment delete` had been left out of the Go port; it is back, and a
test now pins every command of the TypeScript CLI.

A timed-out run is reported once the platform has cancelled it, steps cut
short count as errors, and table columns ignore colour codes. The fake
platform prefers the most specific route and no longer paces requests.
# Conflicts:
#	internal/cli/experiment.go
#	internal/experiment/experiment_test.go
Base automatically changed from spike/go-cli to main September 25, 2026 13:33
…t/go-api-parity

# Conflicts:
#	CHANGELOG.md
#	README.md
#	internal/cli/root.go
#	internal/template/template.go
…ys and environment ids

Environments, teams, their members and environments, integrations, hubs,
property definitions and actions take -t json|yaml; access tokens and
property associations take --output, as --type filters them already.
@achoimet
achoimet merged commit e45dd54 into main Sep 25, 2026
8 checks passed
@achoimet
achoimet deleted the feat/go-api-parity branch September 25, 2026 14:13
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 25, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant