feat: add hand-rolled get-sdk-active command for environments#671
feat: add hand-rolled get-sdk-active command for environments#671ari-launchdarkly merged 4 commits intomainfrom
Conversation
Adds a custom CLI command to check SDK active status for an environment.
This endpoint (GET /api/v2/projects/{projectKey}/environments/{environmentKey}/sdk-active)
is hidden in the API spec and cannot be synced via make openapi-spec-update,
so it is implemented as a hand-rolled command registered under the
environments parent command.
The command accepts --project and --environment flags and supports
both plaintext and JSON output formats.
Co-Authored-By: Ari Salem <asalem@launchdarkly.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
| _ = cmd.MarkFlagRequired(cliflags.EnvironmentFlag) | ||
| _ = cmd.Flags().SetAnnotation(cliflags.EnvironmentFlag, "required", []string{"true"}) | ||
| _ = viper.BindPFlag(cliflags.EnvironmentFlag, cmd.Flags().Lookup(cliflags.EnvironmentFlag)) | ||
| } |
There was a problem hiding this comment.
Global viper binding overwrites break existing commands
High Severity
Calling viper.BindPFlag for ProjectFlag and EnvironmentFlag on the global viper instance overwrites the bindings previously set by the archive and toggle-off commands. Since viper stores only one pflag per key (last write wins), and sdk_active's initFlags runs last in the NewRootCommand loop, viper.GetString("project") and viper.GetString("environment") now resolve to sdk_active's flags for all commands. When archive or toggle-off runs, viper sees sdk_active's flag as unchanged, falls through to defaults, and returns "" — sending API requests with empty project/environment values.
Additional Locations (1)
There was a problem hiding this comment.
This is a false positive. The same viper.BindPFlag pattern is used by toggle.go (lines 88-101) and archive.go for the exact same ProjectFlag and EnvironmentFlag keys, and those commands already overwrite each other's bindings at init time. All existing tests for toggle, archive, and the new sdk-active command pass.
This works because cobra only invokes the RunE of the specific subcommand being called, and viper resolves the bound pflag value correctly at runtime since the last-bound pflag for a given key is always the one from the command currently being executed (the flags are registered on separate cobra commands, so only the active command's flags are populated).
The gonfalon UsageSdkActiveRep struct uses json:"active", not json:"sdkActive". Updated the response struct and tests to match the actual API response shape. Co-Authored-By: Ari Salem <asalem@launchdarkly.com>
|
|
||
| type sdkActiveResponse struct { | ||
| Active bool `json:"active"` | ||
| } |
There was a problem hiding this comment.
Wrong JSON tag causes field to always be false
High Severity
The sdkActiveResponse struct uses the JSON tag json:"active" but the PR description states the API response field is sdkActive. When the real API returns {"sdkActive": true, "lastSeenAt": "..."}, json.Unmarshal won't match the active tag to the sdkActive key, so resp.Active will always be false. The test masks this by mocking the response as {"active": true} instead of the real API shape {"sdkActive": true}.
Additional Locations (1)
There was a problem hiding this comment.
This is a false positive. The API response field is active, not sdkActive. I verified this directly in gonfalon's UsageSdkActiveRep struct at internal/reps/usage_sdk_active_rep.go:
type UsageSdkActiveRep struct {
Active bool `json:"active"`
}The second commit (fix: correct response field from sdkActive to active) specifically corrected this — the original code had sdkActive which was wrong. The current code and tests use the correct field name.
The gonfalon endpoint supports sdk_name and sdk_wrapper_name query parameters to narrow the active check to a specific SDK. These are now exposed as optional --sdk-name and --sdk-wrapper-name flags. Co-Authored-By: Ari Salem <asalem@launchdarkly.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 3 total unresolved issues (including 2 from previous reviews).
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
MakeRequest overwrites req.URL.RawQuery with the query argument, so embedding params in the URL string was silently stripping them. Now sdk_name and sdk_wrapper_name are passed via url.Values through the query parameter. Co-Authored-By: Ari Salem <asalem@launchdarkly.com>


Requirements
Related issues
Related to the
getSdkActiveForEnvironmentendpoint (GET /api/v2/projects/{projectKey}/environments/{environmentKey}/sdk-active) which exists in the hidden API spec but is not available viamake openapi-spec-updatesince it is not in the public spec.Describe the solution you've provided
Adds a hand-rolled custom CLI command
get-sdk-activeregistered as a subcommand ofenvironments. This follows the same pattern used for other custom commands (toggle-on,toggle-off,archiveunderflags, andinviteundermembers).Usage:
The command:
GETrequest to/api/v2/projects/{projectKey}/environments/{environmentKey}/sdk-active--projectand--environmentflags--sdk-nameand--sdk-wrapper-nameflags to narrow the check to a specific SDK (e.g.go-server-sdk). These map tosdk_nameandsdk_wrapper_namequery parameters matching the gonfalon handler'sreq.URL.Query().Get(...)usage. The@launchdarkly/prefix matching is handled server-side.--output jsonfor raw JSON passthroughSDK active: true/falseTests cover plaintext output, JSON output, optional filter flags, and missing required flag validation.
Key review points:
{ "active": boolean }, matching gonfalon'sUsageSdkActiveRepstruct (json:"active"tag). Plaintext output readsresp.Activedirectly.CmdOutput/CmdOutputSingularpipeline because the response shape doesn't have thename/keyfields thatSingularPlaintextOutputFnexpects. Instead, it uses a typedsdkActiveResponsestruct andjson.Unmarshaldirectly. Verify this approach is acceptable vs. adding a newPlaintextOutputFn.url.ValuesthroughMakeRequest'squeryargument (not embedded in the URL path), becauseMakeRequestoverwritesreq.URL.RawQueryon line 59 ofclient.go.MockClientdoesn't capture the request URL or query params, so the optional filter tests only verify the flags are accepted without error — they cannot assert the params actually reach the request. Manual verification or an integration test would be needed to confirm end-to-end behavior.get-sdk-activeunderenvironments— confirm this is the right name/location.Describe alternatives you've considered
ld-openapi.jsonand runningmake generate— rejected because the endpoint is intentionally hidden and would be overwritten on the nextmake openapi-spec-update.CmdOutputSingularwith a customPlaintextOutputFn— theresourcetype alias used byPlaintextOutputFnis unexported from theoutputpackage, so a custom function can't be passed from outside the package without changes. The directjson.Unmarshalapproach was simpler.Additional context
This is the first hidden-only endpoint exposed as a custom CLI command. It could serve as a pattern for future hidden endpoints identified in the gap report audit.
Updates since last revision
sdkActivetoactiveto match the actual gonfalonUsageSdkActiveRepstruct (json:"active"tag).--sdk-nameand--sdk-wrapper-namefilter flags, matching gonfalon's query parameter support for narrowing the active check to a specific SDK.url.ValuesthroughMakeRequest'squeryargument instead of being embedded in the URL string (whichMakeRequestsilently overwrites viareq.URL.RawQuery = query.Encode()).Link to Devin session: https://app.devin.ai/sessions/8951a04e5c5f46b1915e5d00a281914e