Skip to content

[REL-13574] Implement SDK detection and installation#709

Open
dakotasanchez wants to merge 10 commits into
ari-launchdarkly/REL-0000-clifrom
dsanchez/REL-13574/cli
Open

[REL-13574] Implement SDK detection and installation#709
dakotasanchez wants to merge 10 commits into
ari-launchdarkly/REL-0000-clifrom
dsanchez/REL-13574/cli

Conversation

@dakotasanchez
Copy link
Copy Markdown

@dakotasanchez dakotasanchez commented May 12, 2026

Summary

Implements the Detector and Installer interfaces that were stubbed in the base branch, wiring up real filesystem-based SDK detection and package-manager-based installation into the ldcli setup wizard and hidden subcommands.

  • FileDetector — scans the working directory for project indicators and returns language, framework, package manager, recommended SDK ID, and entry point file
  • PackageInstaller — maps SDK IDs to their install commands and shells out to the appropriate package manager
  • APIClients gains Detector and Installer fields for test injection, with FileDetector/PackageInstaller as production defaults
  • Fixed install.go output: Package: pkg@version was printing a trailing @ when Version is empty
  • Fixed cmd/root.go import ordering (gofmt)
  • Manual-install SDKs (Java, Android, Swift) now return meaningful package identifiers (com.launchdarkly:launchdarkly-java-server-sdk etc.) rather than the raw SDK ID

How SDK Detection and Installation Work

Detection (FileDetector)

FileDetector.Detect(dir) runs four probes in priority order against the project directory:

  1. Node.js / React / Next.js — reads package.json and inspects dependencies + devDependencies:
    • Has nextnode-server SDK, framework = "Next.js"
    • Has reactreact-client-sdk, framework = "React"
    • Otherwise → node-server (plain Node)
    • Package manager inferred from lock files: pnpm-lock.yaml → pnpm, yarn.lock → yarn, default → npm
  2. Go — presence of go.modgo-server-sdk
  3. Python — presence of requirements.txt, pyproject.toml, or setup.pypython-server-sdk
  4. Java — presence of pom.xml (→ mvn) or build.gradle/build.gradle.kts (→ gradle) → java-server-sdk

If nothing matches, returns an error; the wizard surfaces it and halts.

Entry point detection walks a prioritized candidate list per SDK (e.g. for React: src/App.tsxsrc/App.jsxsrc/index.tsx → ... → index.js) and returns the first path that exists on disk, or the last candidate as a suggested path if none do. The returned path is absolute and passed directly to Initializer.InjectIntoFile.

Installation (PackageInstaller)

PackageInstaller.Install(dir, detectResult) calls InstallArgs(sdkID, packageManager) which maps each SDK to its install command:

SDK Command
react-client-sdk npm/yarn/pnpm install launchdarkly-react-client-sdk
node-server npm/yarn/pnpm install @launchdarkly/node-server-sdk
python-server-sdk pip install launchdarkly-server-sdk
go-server-sdk go get github.com/launchdarkly/go-server-sdk/v7
ruby-server-sdk gem install launchdarkly-server-sdk
dotnet-server-sdk dotnet add package LaunchDarkly.ServerSdk
Java / Android / Swift no command — Success: false, no error

For SDKs with a command, it shells out via exec.Command in the project directory and captures combined output. A non-zero exit wraps the error and output into the returned error. For SDKs without a command, it returns Success: false with no error so the wizard proceeds (the user still needs to add the dependency manually, but flag creation and code injection remain valid).

Wizard Integration

selectProject → selectEnvironment → fetchEnvDetails
                                          │
                                          ▼
                                      stepDetect  ←── FileDetector.Detect(cwd)
                                          │              → SDKID, PackageManager, EntryPoint
                                          ▼
                                      stepInstall ←── PackageInstaller.Install(cwd, detectResult)
                                          │              → shells out: npm install / go get / pip install / ...
                                          ▼
                                      stepCreateFlag → stepInit → stepWaitForApp → stepVerify → stepDone

detectResult.SDKID flows into both the installer (command selection) and the initializer (template selection). detectResult.EntryPoint is passed directly to InjectIntoFile. All five SDKs the detector can return have init templates, so the stepWaitForApp file path display is always valid.

Test plan

  • go build ./... passes
  • go test ./internal/setup/... — 26 tests covering all detection scenarios, package managers, entry point fallback, firstExistingIn edge cases, InstallArgs for all SDK types, and PackageInstaller with mock runner
  • go test ./cmd/setup/... — detect error/success/JSON, install error/success/JSON/version paths
  • go test ./... — all 28 packages pass
  • gofmt clean on all changed files
  • golangci-lint — no new issues introduced (6 pre-existing errcheck findings in ari's files)
  • Manual: ldcli setup detect --path /path/to/react/project returns language/framework/SDK info
  • Manual: ldcli setup detect --path /tmp/empty returns "could not detect" error
  • Manual: ldcli setup install --sdk-id node-server runs npm install @launchdarkly/node-server-sdk
  • Manual: ldcli setup install --sdk-id java-server-sdk returns Success: false with package com.launchdarkly:launchdarkly-java-server-sdk

Requirements

  • I have added test coverage for new or changed functionality
  • I have followed the repository's pull request submission guidelines
  • I have validated my changes against all supported platform versions

Note

Medium Risk
Adds real project detection and shells out to system package managers during ldcli setup, which can affect local environments and introduces platform/command-failure edge cases. Wizard flow changes also alter user interaction and error handling paths.

Overview
ldcli setup now uses real implementations for SDK detection and installation: internal/setup.FileDetector infers language/framework/package manager/entry point from project files, and internal/setup.PackageInstaller maps SDK IDs to package-manager commands and executes them (with manual-install SDKs returning Success=false plus meaningful package identifiers).

The setup wizard flow is updated to include an explicit SDK selection step (prioritizing detected SDK, falling back when detection fails) and to deep-link the final “toggle your flag” URL. Initialization handling is tightened by correctly mapping android-client-sdk and by merging Go imports via AST rewriting to avoid breaking package declarations.

CLI wiring and output are adjusted: cmd.APIClients gains injectable Detector/Installer with production defaults, setup install no longer prints a trailing @ when no version is present, and extensive new tests cover detection/install behavior and wizard transitions.

Reviewed by Cursor Bugbot for commit 32f1834. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread internal/setup/detector.go Outdated
Copy link
Copy Markdown
Contributor

@ari-launchdarkly ari-launchdarkly left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really great work. I've got questions around whether we can make this a little more deterministic around some of the edges if we're leaning away from an agent to determine some of the dependencies. If it's not abundantly obvious for us, we can also punt on that

Comment thread internal/setup/detector.go
Comment thread internal/setup/detector.go
Comment thread internal/setup/detector.go
Comment thread internal/setup/detector.go
Comment thread internal/setup/detector.go
Comment thread internal/setup/detector.go
Comment thread internal/setup/installer.go
Comment thread internal/setup/installer.go Outdated
Comment thread internal/setup/installer.go
Comment thread internal/setup/installer.go
Comment thread internal/setup/detector.go
Comment thread internal/setup/detector.go Outdated
Comment thread internal/setup/detector.go Outdated
@dakotasanchez dakotasanchez force-pushed the dsanchez/REL-13574/cli branch from 71c2501 to 2e055cf Compare May 13, 2026 19:09
Comment thread cmd/setup/wizard.go
Comment thread internal/setup/detector.go
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default mode and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f60f9d1. Configure here.

Comment thread cmd/setup/wizard.go
@dakotasanchez dakotasanchez force-pushed the dsanchez/REL-13574/cli branch from f60f9d1 to d3d26dc Compare May 14, 2026 18:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants