[REL-13574] Implement SDK detection and installation#709
Conversation
ari-launchdarkly
left a comment
There was a problem hiding this comment.
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
| if _, ok := allDeps["next"]; ok { | ||
| return &DetectResult{ | ||
| Language: "JavaScript", | ||
| Framework: "Next.js", | ||
| PackageManager: pm, | ||
| SDKID: "node-server", | ||
| EntryPoint: filepath.Join(dir, firstExistingIn(dir, []string{ | ||
| "src/index.ts", "src/index.js", | ||
| "pages/index.tsx", "pages/index.ts", "pages/index.js", | ||
| "index.js", | ||
| })), | ||
| } | ||
| } | ||
|
|
||
| if _, ok := allDeps["react"]; ok { | ||
| return &DetectResult{ | ||
| Language: "JavaScript", | ||
| Framework: "React", | ||
| PackageManager: pm, | ||
| SDKID: "react-client-sdk", | ||
| EntryPoint: filepath.Join(dir, firstExistingIn(dir, []string{ | ||
| "src/App.tsx", "src/App.jsx", "src/App.js", | ||
| "src/index.tsx", "src/index.jsx", "src/index.js", | ||
| "index.js", | ||
| })), | ||
| } | ||
| } |
There was a problem hiding this comment.
can we flesh these out to account for the:
https://launchdarkly.com/docs/sdk/client-side/javascript
https://launchdarkly.com/docs/sdk/client-side/react/react-native
There was a problem hiding this comment.
It's pretty tough to detect just plain JS with no framework, so maybe the SDK selector can just take care of that case.
There was a problem hiding this comment.
Maybe. I think we can try and detect some of those based on known active frameworks. I think I put in the suggestion downstream
There was a problem hiding this comment.
Here's what we're doing now:
- Has next dep → node-server
- Has react-native dep → react-native
- Has react dep → react-client-sdk
- Has backbone/svelte/vue/angular/ember/preact dep → js-client-sdk
- Fallback: anything else with a package.json → node-server
| case "android", "android-client-sdk": | ||
| return nil, "com.launchdarkly:launchdarkly-android-client-sdk" |
There was a problem hiding this comment.
for this, the android SDK has a couple different ways of setting it depending on whether the user is using Kotlin or Groovy:
https://launchdarkly.com/docs/sdk/client-side/android#install-the-sdk
Is there a way for us to account for the package here?
There was a problem hiding this comment.
We're accounting for Kotlin vs. Groovy, but we should surface to the user that they need to add the implementation 'com.launchdarkly:launchdarkly-android-client-sdk:5.+' to their build file. Unless we want to inject code here as well?
| case "java-server-sdk": | ||
| return nil, "com.launchdarkly:launchdarkly-java-server-sdk" |
There was a problem hiding this comment.
for this, the docs point to us having a way to do this in XML and Gradle:
https://launchdarkly.com/docs/sdk/server-side/java#install-the-sdk
Is that something we'd want to be deterministic about?
There was a problem hiding this comment.
We're deterministic about this detection, but we still are prompting the user to copy/paste a snippet for Gradle/Maven, since there's no shell command to import the LD SDK like with the other languages.
Co-authored-by: ari-launchdarkly <asalem@launchdarkly.com>
Co-authored-by: ari-launchdarkly <asalem@launchdarkly.com>
Co-authored-by: ari-launchdarkly <asalem@launchdarkly.com>
Co-authored-by: ari-launchdarkly <asalem@launchdarkly.com>
71c2501 to
2e055cf
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default mode and found 1 potential issue.
❌ 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.
| m.detectedEntryPoint = msg.result.EntryPoint | ||
| m.sdkList = m.buildSDKList(msg.result.SDKID) | ||
| m.step = stepSelectSDK | ||
| return m, nil |
There was a problem hiding this comment.
Detected package manager discarded during SDK selection
High Severity
The detectDoneMsg handler stores EntryPoint in m.detectedEntryPoint but discards PackageManager. When the user confirms SDK selection in handleEnter, the new DetectResult is constructed without PackageManager, leaving it as an empty string. This causes InstallArgs → resolveNodePM("") to always default to "npm", even when the detector correctly identified yarn, pnpm, or bun from lock files. The installer will run the wrong package manager command (e.g. npm install in a pnpm project).
Additional Locations (1)
Reviewed by Cursor Bugbot for commit f60f9d1. Configure here.


Summary
Implements the
DetectorandInstallerinterfaces that were stubbed in the base branch, wiring up real filesystem-based SDK detection and package-manager-based installation into theldcli setupwizard and hidden subcommands.FileDetector— scans the working directory for project indicators and returns language, framework, package manager, recommended SDK ID, and entry point filePackageInstaller— maps SDK IDs to their install commands and shells out to the appropriate package managerAPIClientsgainsDetectorandInstallerfields for test injection, withFileDetector/PackageInstalleras production defaultsinstall.gooutput:Package: pkg@versionwas printing a trailing@whenVersionis emptycmd/root.goimport ordering (gofmt)com.launchdarkly:launchdarkly-java-server-sdketc.) rather than the raw SDK IDHow SDK Detection and Installation Work
Detection (
FileDetector)FileDetector.Detect(dir)runs four probes in priority order against the project directory:package.jsonand inspectsdependencies+devDependencies:next→node-serverSDK, framework = "Next.js"react→react-client-sdk, framework = "React"node-server(plain Node)pnpm-lock.yaml→ pnpm,yarn.lock→ yarn, default → npmgo.mod→go-server-sdkrequirements.txt,pyproject.toml, orsetup.py→python-server-sdkpom.xml(→ mvn) orbuild.gradle/build.gradle.kts(→ gradle) →java-server-sdkIf 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.tsx→src/App.jsx→src/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 toInitializer.InjectIntoFile.Installation (
PackageInstaller)PackageInstaller.Install(dir, detectResult)callsInstallArgs(sdkID, packageManager)which maps each SDK to its install command:react-client-sdknpm/yarn/pnpm install launchdarkly-react-client-sdknode-servernpm/yarn/pnpm install @launchdarkly/node-server-sdkpython-server-sdkpip install launchdarkly-server-sdkgo-server-sdkgo get github.com/launchdarkly/go-server-sdk/v7ruby-server-sdkgem install launchdarkly-server-sdkdotnet-server-sdkdotnet add package LaunchDarkly.ServerSdkSuccess: false, no errorFor SDKs with a command, it shells out via
exec.Commandin 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 returnsSuccess: falsewith 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
detectResult.SDKIDflows into both the installer (command selection) and the initializer (template selection).detectResult.EntryPointis passed directly toInjectIntoFile. All five SDKs the detector can return have init templates, so thestepWaitForAppfile path display is always valid.Test plan
go build ./...passesgo test ./internal/setup/...— 26 tests covering all detection scenarios, package managers, entry point fallback,firstExistingInedge cases,InstallArgsfor all SDK types, andPackageInstallerwith mock runnergo test ./cmd/setup/...— detect error/success/JSON, install error/success/JSON/version pathsgo test ./...— all 28 packages passgofmtclean on all changed filesgolangci-lint— no new issues introduced (6 pre-existingerrcheckfindings in ari's files)ldcli setup detect --path /path/to/react/projectreturns language/framework/SDK infoldcli setup detect --path /tmp/emptyreturns "could not detect" errorldcli setup install --sdk-id node-serverrunsnpm install @launchdarkly/node-server-sdkldcli setup install --sdk-id java-server-sdkreturnsSuccess: falsewith packagecom.launchdarkly:launchdarkly-java-server-sdkRequirements
Note
Medium Risk
Adds real project detection and shells out to system package managers during
ldcli setup, which can affect local environments and error handling. Wizard flow changes (manual SDK selection fallback) introduce new state transitions but are covered by tests.Overview
ldcli setupnow uses real implementations for SDK detection and installation: a new filesystem-basedFileDetectorinfers language/framework/package manager/entry point from common project files, andPackageInstallermaps SDK IDs to install commands and runs them via the system package manager.The setup wizard flow changes to always present an SDK selection step, prioritizing the detected SDK but allowing override (and falling back to manual selection when detection fails), and
APIClientsgains injectableDetector/Installerwith production defaults wired incmd/root.go. Output and template handling were tightened (avoid trailing@when version is empty; align Android template ID toandroid-client-sdk; adjust Go template import guidance), with extensive new/updated tests for detection, install command selection/execution, and wizard transitions.Reviewed by Cursor Bugbot for commit f60f9d1. Bugbot is set up for automated code reviews on this repo. Configure here.