DRAFT: cli: entire ci buildkite org connect/list/cluster (M1)#1758
DRAFT: cli: entire ci buildkite org connect/list/cluster (M1)#1758dvydra wants to merge 1 commit into
Conversation
Mount an `org` subgroup under `entire ci buildkite` (internal-gated) for org/project admins to connect their own Buildkite organization and register its hosted clusters: - `org connect <entire-org> --bk-org <slug>` — POST the BK API token (from --bk-token, BUILDKITE_API_TOKEN, or a no-echo prompt). The token is sent over TLS only, never echoed or logged; only token_len is reported. - `org list <entire-org> [--json]` — GET the connected credentials plus each BK org's registered clusters. No token material rendered. - `org cluster add <entire-org> --bk-org --bk-cluster-id [--auth-plugin-ref]` — POST a hosted-cluster registration (idempotent upsert). - `org disconnect <entire-org> --bk-org <slug>` — DELETE the credential. Org resolution mirrors ResolveNativeRepo: a raw org ULID passes through, a name resolves via the control plane's ?name= filter (new ResolveOrg helper). These endpoints live in the DRAFT backend entiredb#2744 and are not yet in the generated coreapi spec, so the requests are hand-rolled via the escape hatch: PostJSON/DeleteJSON added alongside GetJSON (same serverURL, bearer, and transport, no second auth path). Swap to the generated client once entiredb#2744 merges and the spec regenerates. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CHPhjYDtZx71THMtVwc83E
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 4830786. Configure here.
| func resolveBuildkiteToken(cmd *cobra.Command, flagToken string) (string, error) { | ||
| if strings.TrimSpace(flagToken) != "" { | ||
| fmt.Fprintln(cmd.ErrOrStderr(), "warning: --bk-token is visible in your shell history; prefer BUILDKITE_API_TOKEN or the interactive prompt") | ||
| return flagToken, nil |
There was a problem hiding this comment.
Flag token keeps surrounding whitespace
Medium Severity
When --bk-token is non-blank after trim, resolveBuildkiteToken returns the raw flag value instead of the trimmed string. Leading or trailing spaces are sent in bk_api_token, which can cause Buildkite to reject a otherwise valid token.
Reviewed by Cursor Bugbot for commit 4830786. Configure here.
| cmd.SilenceUsage = true | ||
| return err | ||
| } | ||
| return runCore(cmd, func(ctx context.Context, c *coreapi.Client) error { |
There was a problem hiding this comment.
Buildkite token before Entire login
Low Severity
org connect resolves the Buildkite API token (including an interactive no-echo prompt) before runCore establishes an Entire control-plane session. If the user is not logged in, they may enter a secret first and only then see an authentication failure from the CLI.
Reviewed by Cursor Bugbot for commit 4830786. Configure here.
There was a problem hiding this comment.
Pull request overview
Adds the internal-only entire ci buildkite org command surface for connecting/listing/disconnecting Buildkite org credentials and registering hosted clusters, backed by draft org-scoped core endpoints (pre–coreapi spec regen) via new coreapi.Client JSON escape hatches.
Changes:
- Extend
internal/coreapi.ClientwithPostJSONandDeleteJSONescape hatches to call draft endpoints while reusing existing auth/transport behavior. - Add
ResolveOrg(ULID passthrough or case-insensitive by-name lookup) alongside the existing CI repo resolver. - Introduce internal-gated
entire ci buildkite org {connect,list,cluster add,disconnect}commands plushttptestcoverage for request wiring and secret-hygiene expectations.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/coreapi/client.go | Adds PostJSON/DeleteJSON escape hatches (temporary until OpenAPI regen) reusing the core client’s auth + transport. |
| cmd/entire/cli/ci/resolve.go | Adds ResolveOrg and generalizes ULID shape checking for org + repo refs. |
| cmd/entire/cli/ci/resolve_test.go | Adds unit tests for ResolveOrg ULID passthrough, name lookup, empty ref, and unknown name. |
| cmd/entire/cli/ci/buildkite_org_internal.go | Implements internal-only ci buildkite org verbs, token sourcing/prompting, and human/JSON renderers. |
| cmd/entire/cli/ci/buildkite_org_internal_test.go | Adds internal-tagged command tests using httptest, including assertions that token material never appears in output. |
| cmd/entire/cli/ci/buildkite_internal.go | Wires the new buildkite org subgroup into the existing internal buildkite command tree. |
| func promptBuildkiteToken(cmd *cobra.Command) (string, error) { | ||
| fmt.Fprint(cmd.ErrOrStderr(), "Buildkite API token: ") | ||
| raw, err := term.ReadPassword(int(os.Stdin.Fd())) //nolint:gosec // G115: uintptr->int is safe for a stdin fd | ||
| fmt.Fprintln(cmd.ErrOrStderr()) // terminate the (hidden) input line | ||
| if err != nil { | ||
| return "", fmt.Errorf("read Buildkite API token: %w", err) | ||
| } | ||
| token := strings.TrimSpace(string(raw)) | ||
| if token == "" { | ||
| return "", errors.New("no Buildkite API token entered") | ||
| } | ||
| return token, nil |
| if strings.TrimSpace(flagToken) != "" { | ||
| fmt.Fprintln(cmd.ErrOrStderr(), "warning: --bk-token is visible in your shell history; prefer BUILDKITE_API_TOKEN or the interactive prompt") | ||
| return flagToken, nil | ||
| } |
|
Superseded. entire-ci now ships as a standalone plugin — https://github.com/entireio/entire-ci — discovered by the CLI as |


Why
M1 of the customer-facing CI-webhooks surface (COR-976): an org/project admin
needs to connect their own Buildkite organization to an Entire org — supply a
Buildkite API token (stored encrypted server-side) and register the hosted
clusters their pipelines run on. This adds the CLI verbs for that flow.
The backend endpoints this drives live in the DRAFT backend
entiredb#2744(branchdv/ci-org-credentials-m1) and are not yet in thegenerated
coreapispec. This PR is therefore also a DRAFT — do notmerge until
entiredb#2744lands and passes security review.This PR stacks on
#1756→#1755→#1754→#1753(base branchdv/ci-buildkite-watch). Review/merge those first.What
Mounts an
orgsubgroup underentire ci buildkite(internal build tag only,absent from released binaries):
entire ci buildkite org connect <entire-org> --bk-org <slug> [--bk-token <t>]→
POST /api/v1/orgs/{orgId}/ci/buildkite/credentialwith body{bk_organization, bk_api_token}. PrintsConnected(201) /Rotated(200) plus
token_len; the token is never echoed.--jsonsupported.entire ci buildkite org list <entire-org> [--json]→
GET .../credentials, thenGET .../clusters?bk_organization=<slug>perconnected credential. Renders a credentials table + a clusters table (no
token material — only
token_len).entire ci buildkite org cluster add <entire-org> --bk-org <slug> --bk-cluster-id <id> [--auth-plugin-ref <s>]→
POST .../clusters. PrintsRegistered(201) /Updated(200).entire ci buildkite org disconnect <entire-org> --bk-org <slug>→
DELETE .../credential/{bkOrg}.Org resolution.
<entire-org>accepts an org ULID (passed through after ashape check, no network call) or an org name (resolved via the control plane's
case-insensitive
?name=filter). NewResolveOrghelper next toResolveNativeRepo, mirroring its ULID-or-name contract; distinctno org named …error for an unknown name.Token handling. The Buildkite API token is sourced, in order, from
--bk-token(with a one-line warning that it lands in shell history), theBUILDKITE_API_TOKENenv var, then an interactive no-echo prompt(
term.ReadPassword) when a TTY is available. It is sent to the server overthe existing TLS client only — never logged, never echoed, and absent from all
command output (only
token_lensurfaces).Calling the draft backend. These endpoints aren't in the generated client
yet, so the requests are hand-rolled by extending the C3 escape hatch:
PostJSON/DeleteJSONadded alongsideGetJSONininternal/coreapi/client.go, reusing the sameserverURL, bearerSecuritySource, and cross-jurisdiction transport (no second auth path).PostJSONreturns the status code so the verbs can distinguish 201-createdfrom 200-updated. Both carry a removal note: swap these calls for the
generated Invoker methods once
entiredb#2744merges and the specregenerates, then delete the local DTOs + escape-hatch helpers.
Verification
go build ./...andgo build -tags internal ./...both pass.httptest):resolve_test.go(untagged):ResolveOrgULID passthrough (zero HTTPcalls), name lookup (server-side
?name=), unknown-name error, empty ref.buildkite_org_internal_test.go(internal-tagged): connect body assembly +Connected/Rotatedverbs + token never on stdout/stderr,--bk-tokenshell-history warning, no-token-source failure,
--bk-orgrequired; listtable +
--json(no token material) + empty case (clusters not queried);cluster add body +
Registered/Updated+ required flags; disconnect path.go test ./cmd/entire/cli/ci/... && go test -tags internal ./cmd/entire/cli/ci/...and
go test ./internal/coreapi/...all green.mise run fmtclean;golangci-lint run --build-tags internal ./cmd/entire/cli/ci/...and full
mise run lintreport 0 issues.go build -tags internal ./cmd/entirethenentire ci buildkite org {connect,list,cluster add,disconnect} --helpallrender.
Follow-ups
coreapispec onceentiredb#2744merges, switch the fourverbs to the generated Invoker methods, and delete
PostJSON/DeleteJSONand the local DTOs.
🤖 Generated with Claude Code
https://claude.ai/code/session_01CHPhjYDtZx71THMtVwc83E
Note
High Risk
Handles Buildkite API tokens (collection, transport, and shell-history exposure) and depends on draft backend APIs via hand-rolled HTTP until spec regeneration; mistakes could leak credentials or misconfigure org CI auth.
Overview
Adds an internal-only
entire ci buildkite orgcommand group so org admins can connect Buildkite API credentials, list credentials and registered hosted clusters, register/update clusters, and disconnect credentials against draft org-scoped/api/v1/orgs/{orgId}/ci/buildkite/…endpoints.Org targeting introduces
ResolveOrg(ULID passthrough or name via?name=) and renames the shared ULID shape helper tolooksLikeULIDfor repo and org refs.API client extends the
coreapiescape hatch withPostJSONandDeleteJSON(alongside existingGetJSON) until the OpenAPI spec includes these routes; verbs use local DTOs and distinguish 201 vs 200 in user output.Token handling for
org connectprefers env or a no-echo prompt over--bk-token, warns when the flag is used, and never prints token material (onlytoken_len). Tests cover HTTP wiring, validation, and secret hygiene.Reviewed by Cursor Bugbot for commit 4830786. Configure here.