DataRobot CLI (dr) - A Go-based command-line interface for managing DataRobot custom applications with OAuth integration, template management, and task execution capabilities.
Use Taskfile tasks rather than raw Go commands:
| Command | Description |
|---|---|
task build |
Build the CLI binary (outputs to ./dist/dr) |
task lint |
Check for lint issues (read-only) |
task delint |
Fix lint and formatting issues |
task test |
Run tests with race detection and coverage |
task dev-init |
Initialize development environment |
task run |
Run CLI directly via go run |
- Run tests:
task test - Tests use
testify/assertfor assertions - Test files follow
*_test.gonaming convention - If DR_API_TOKEN is set, run smoke tests:
task smoke-test(but ask for permission before using a real API token)
Go Version Requirement: Tests run with the -race flag for data race detection. The race runtime must match your Go compiler version exactly. If you see errors like compile: version "go1.X.Y" does not match go tool version "go1.X.Z", ensure your installed Go version matches the version in go.mod (run brew upgrade go or adjust go.mod accordingly).
- Commands must use singular names (e.g.,
template,dependency,plugin) - Plural aliases are acceptable for backward compatibility (e.g.,
templates,dependencies,plugins) - Maintain consistency across all top-level and sub-commands
All code must pass golangci-lint with zero errors. Follow these whitespace rules:
- Never cuddle declarations - Always add a blank line before
var,const,typedeclarations when they follow other statements - Separate statement types - Add blank lines between different statement types (assign, if, for, return, etc.)
- Blank line after block start - Add blank line after opening braces of functions/blocks when followed by declarations
- Blank line before multi-line statements - Add blank line before if/for/switch statements
Example of correct spacing:
func example() {
x := 1
if x > 0 {
y := 2
fmt.Println(y)
}
var result string
result = "done"
return result
}- Always use
tui.Run()to execute TUI models for global Ctrl-C handling and debug logging - Always wrap new TUI models with the InterruptibleModel from the
tuipackage to ensure global Ctrl-C handling - Reuse existing TUI components from
tuipackage or Bubbles library (https://github.com/charmbracelet/bubbles) - Use styles from
tui/styles.gofor consistency - When
--debugis enabled, logs are written to.dr-tui-debug.log
task lint (check-only, non-modifying):
go mod tidy -diff- checks if go.mod/go.sum need updatesgofumpt -l -d- lists formatting issues and shows diffsgo vet- checks for suspicious constructsgolangci-lint run- comprehensive linting checks (includes wsl, revive, staticcheck)goreleaser check- validates release configuration
task delint (auto-fixes):
go mod tidy- fixes go.mod/go.sumgo fmt- fixes basic formattinggofumpt -l -w- fixes aggressive Go formattinggolangci-lint run --fix- fixes linting issues where possible
All code must pass task lint before submitting.
When upgrading the Go version in go.mod, you may need to update golangci-lint to ensure compatibility:
- Check the golangci-lint releases for a version that supports your target Go version
- Update the
GOLANGCI_LINT_VERSIONvariable inTaskfile.yaml - Run
task install-toolsto download the pre-built binary - Run
task delintto auto-fix any issues, thentask lintto verify compatibility
The GOLANGCI_LINT_VERSION is pinned to ensure reproducible builds across all development environments. The binary is installed as a standalone pre-built artifact, not via go install, so version mismatches between your project's Go version and golangci-lint's internal Go version are handled automatically.
The CLI persists user configuration to drconfig.yaml via viper. To prevent
transient command flags from leaking into the config file, follow these rules.
For full details, see docs/development/configuration.md.
Quick reference:
- Outside
internal/config/, do not importgithub.com/spf13/viperdirectly. Useinternal/config/viperx. Direct imports are blocked bydepguard. - Never call
viper.WriteConfig()directly (andviperxdoes not expose it). Useconfig.UpdateConfigFile(keys ...string), which only writes keys listed inconfig.PersistableKeys(internal/config/write.go). - Never bulk-bind subcommand flags to viper.
viperxdoes not exposeBindPFlags. Bind only specific persistent flags explicitly viaviperx.BindPFlagincmd/root.go::init(). - Read transient flags directly from cobra:
cmd.Flags().GetBool("yes"). Do not bind them withviperx.BindPFlag. - Env-var override for a transient flag: register only the env var via
viperx.BindEnv(key, "DATAROBOT_CLI_…")and OR the two sources at the call site:yesFlag, _ := cmd.Flags().GetBool("yes"); yes := yesFlag || viperx.GetBool("yes"). - To make a key persistable, add it to
config.PersistableKeysand have the write site callconfig.UpdateConfigFile("my-key").
All PRs are reviewed against bugbot rules in .cursor/BUGBOT.md. Rules are organized by risk level:
High-Risk (catches silent failures, data corruption, poor error handling):
- Concurrency — Goroutine panic recovery, loop variable capture, WaitGroup pairing, channel closure
- Error Handling — Wrapping errors with context, user-facing messages, not silently ignoring errors
- Security — Input validation, security boundaries, threat models
- Testing — Race detector, error path coverage, test seams
Resource & Operations (prevents hangs, leaks, platform bugs):
- Resources — Lock lifecycle, timeouts, cleanup, disk space checks
- Paths — Validation, normalization, Unicode, symlinks
- Cross-Platform — Build tags, case sensitivity, line endings, symlink handling
Design (prevents tight coupling, premature abstraction):
- Architecture — Code organization, separation of concerns, dependency injection, phase orchestration
- Package APIs — Contracts between packages, documentation, limitations
Quality (consistency and maintainability):
- Testing — Test coverage, mocking, pagination tests
- Commands — Table rendering, file organization, output formatting
When working on code, apply these quick principles:
- Concurrency: Recover from panics, capture loop variables, pair WaitGroups, close channels safely
- Errors: Wrap with context, specialize messages (404 vs 500), log before returning
- Commands: Use
lipgloss/table+tui.TableBorderStyle, consistent styling, test output formatting - Platforms: Add build tags, match signatures, test on target platforms
Refer to .cursor/BUGBOT.md for detailed rules and examples during PR review.
Feature gates allow commands to be hidden until ready for release. For comprehensive documentation including implementation details, see docs/development/feature-gates.md.
Quick reference:
- Gate a command via
features.SetGate(cmd, "feature-name")(sets the annotation on the command) - Enable via env var:
DATAROBOT_CLI_FEATURE_<NAME>=true(e.g.,DATAROBOT_CLI_FEATURE_WORKLOAD=true) - Currently supported: environment variables only (config file support planned)
- Filtering happens via
cli.CommandAdder.AddCommandat registration time —CommandAdderis the only filtering mechanism - To gate a nested subcommand, wrap the parent with
&cli.CommandAdder{Command: parent}and calladder.AddCommand(...)instead ofparent.AddCommand(...)
Output change summaries in Markdown format using the template in .github/PULL_REQUEST_TEMPLATE.md.