diff --git a/astro.config.mjs b/astro.config.mjs index 19e559c..5671976 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -116,6 +116,20 @@ export default defineConfig({ { label: "OpenTelemetry", slug: "docs/integrations/opentelemetry" }, ], }, + { + label: "Go CLI", + collapsed: true, + items: [ + { label: "Overview", slug: "docs/go" }, + { label: "Quickstart", slug: "docs/go/quickstart" }, + { label: "scan", slug: "docs/go/cli/scan" }, + { label: "audit", slug: "docs/go/cli/audit" }, + { label: "validate", slug: "docs/go/cli/validate" }, + { label: "Identity Model", slug: "docs/go/concepts/identity-model" }, + { label: "Supported Scope", slug: "docs/go/reference/supported-scope" }, + { label: "Limitations", slug: "docs/go/reference/limitations" }, + ], + }, { label: "Trust", collapsed: true, diff --git a/src/content/docs/docs/concepts/safety-model.md b/src/content/docs/docs/concepts/safety-model.md index 2602cb1..2f21c69 100644 --- a/src/content/docs/docs/concepts/safety-model.md +++ b/src/content/docs/docs/concepts/safety-model.md @@ -1,9 +1,11 @@ --- title: Safety Model description: What FlagLint auto-rewrites, what it skips, and why the safety-over-coverage principle guides every pattern. -lastUpdated: 2026-06-22 +lastUpdated: 2026-07-06 --- +This page covers **flaglint-js**'s `migrate --apply` rewrite safety model, which is specific to the JavaScript/TypeScript CLI — flaglint-go does not have an automated migration/rewrite command (audit, scan, and validate only). See [flaglint-go's concepts docs](/docs/go/concepts/identity-model/) for the Go CLI's own safety guarantees. + FlagLint is conservative by design. It separates inventory, review, and source edits so teams can inspect every migration change before it lands in production. ## Safety Model Diagram @@ -45,7 +47,8 @@ When every condition is met, FlagLint produces an idempotent, argument-order-cor | **Wrappers not configured in `.flaglintrc`** | FlagLint cannot distinguish a custom wrapper from an unrelated function unless it is explicitly declared | Add wrapper declarations to `wrappers: []` in `.flaglintrc`, then re-run | | **Ambiguous fallback types** — e.g. `null`, a variable, or a union-typed expression | FlagLint cannot determine which `get*Value` method to use without a concrete literal type | Replace the fallback with an explicit literal (`false`, `""`, `0`, `{}`) at the call site | | **Non-LaunchDarkly providers** | FlagLint is a LaunchDarkly-specific static analysis tool | No action; these calls are reported in the inventory but never touched | -| **Non-Node.js SDKs** — browser SDK, Go SDK, Java SDK | Only the Node.js server-side SDK import provenance is verified | Migrate browser and other-language calls manually | +| **Non-Node.js SDKs** — browser SDK, Java SDK | Only the Node.js server-side SDK import provenance is verified | Migrate browser and other-language calls manually | +| **Go SDK** | flaglint-js only verifies the Node.js server-side SDK import — Go usage is out of scope for this tool entirely | Use [flaglint-go](/docs/go/) to audit Go usage; it has no `--apply`-style rewrite yet, only `scan`/`audit`/`validate` | ## Why Fallback Value Does Not Equal Production Value diff --git a/src/content/docs/docs/go/cli/audit.md b/src/content/docs/docs/go/cli/audit.md new file mode 100644 index 0000000..fe7c8bc --- /dev/null +++ b/src/content/docs/docs/go/cli/audit.md @@ -0,0 +1,84 @@ +--- +title: flaglint-go audit +description: Risk-ranked LaunchDarkly Go SDK usage report with a migration-readiness score. +lastUpdated: 2026-07-06 +--- + +`flaglint-go audit` scans your source and classifies every detected LaunchDarkly Go SDK call by risk level, producing a migration-readiness score. It never modifies files. + +## Command + +```bash +flaglint-go audit [dir] [flags] +``` + +## Options + +| Flag | Description | +| --- | --- | +| `-f, --format string` | Output format: `json` \| `markdown` (default `markdown`) | +| `-o, --output string` | Write report to a file instead of stdout | +| `--config string` | Path to config file | +| `--write-baseline string` | Write current finding fingerprints to a baseline file for use with `validate --baseline --fail-on-new` | + +## Risk Levels + +| Method | Risk | Notes | +|---|---|---| +| `BoolVariation` / `StringVariation` / `IntVariation` / `Float64Variation` (and their `*Ctx` forms) | Low | Static key only | +| `JSONVariation` (and `*Ctx`) | Medium | Pointer output, manual review | +| `*VariationDetail(Ctx)` methods | High | Returns `EvaluationDetail` — no direct OpenFeature equivalent | +| `AllFlagsState` | High | Bulk call, no single-flag equivalent | +| Dynamic flag key (identifier, `fmt.Sprintf`, string concatenation) | High | Cannot resolve statically — overrides the method's own risk level | + +## Example Output + +```text +Scan complete — 2 unique flag(s) across 2 call site(s) (0ms, 2 file(s)) +Migration readiness: 100/100 · ready + 2 low risk · 0 medium risk · 0 high risk +``` + +A codebase with genuine high-risk usage: + +```text +Scan complete — 0 unique flag(s) across 4 call site(s) (2.3s, 4512 file(s)) +Migration readiness: 0/100 · complex + 0 low risk · 0 medium risk · 4 high risk +``` + +(All four call sites use a dynamic, per-instance flag key — correctly classified high risk regardless of the underlying method, per the dynamic-key rule above.) + +## Migration Readiness + +The score is the ratio of low/medium-risk (safely reviewable) call sites to total detected call sites, expressed 0–100. `N/A` is reported when zero direct calls are found — there's nothing to score. + +## Baseline Mode (`--write-baseline`) + +```bash +flaglint-go audit ./services --write-baseline .flaglint-baseline.json +``` + +```json +{ + "version": "1", + "createdAt": "2026-07-06T19:23:09Z", + "flaglintVersion": "dev", + "fingerprints": [ + "launchdarkly:BoolVariation:checkout-v2:checkout.go", + "launchdarkly:IntVariation:discount-percentage:checkout.go" + ] +} +``` + +Commit this file to source control, then use it with `flaglint-go validate --baseline --fail-on-new` to adopt CI enforcement without requiring all existing debt to be fixed first. See [Quickstart: Adopt Gradually With a Baseline](/docs/go/quickstart/#4-adopt-gradually-with-a-baseline). + +## Exit Behavior + +`audit` always exits `0` unless the tool itself errors. It is informational — use `flaglint-go validate --no-direct-launchdarkly` to fail a build. + +## Feedback + +- [Edit this page on GitHub](https://github.com/flaglint/flaglint.dev/edit/main/src/content/docs/docs/go/cli/audit.md) +- [Report an issue](https://github.com/flaglint/flaglint-go/issues/new) +- Next: [validate](/docs/go/cli/validate/) diff --git a/src/content/docs/docs/go/cli/scan.md b/src/content/docs/docs/go/cli/scan.md new file mode 100644 index 0000000..0f8f452 --- /dev/null +++ b/src/content/docs/docs/go/cli/scan.md @@ -0,0 +1,74 @@ +--- +title: flaglint-go scan +description: Structured, file-level LaunchDarkly Go SDK usage inventory. Always exits 0. +lastUpdated: 2026-07-06 +--- + +`flaglint-go scan` walks a directory and reports every detected LaunchDarkly Go SDK call site as a structured inventory — no risk scoring, no readiness score, just the raw findings. Use `audit` instead when you want a risk-ranked summary. + +## Command + +```bash +flaglint-go scan [dir] [flags] +``` + +## Options + +| Flag | Description | +| --- | --- | +| `-f, --format string` | Output format: `json` \| `markdown` (default `markdown`) | +| `-o, --output string` | Write report to a file instead of stdout | +| `--config string` | Path to config file | + +## Example Output (Markdown) + +```text +# FlagLint Go Scan Report + +Scanned 2 file(s) in 0ms — 2 unique flag(s) across 2 call site(s). + +## Flags + +| Flag | Call Type | Risk | File | +|---|---|---|---| +| `checkout-v2` | BoolVariation | low | checkout.go:13 | +| `discount-percentage` | IntVariation | low | checkout.go:20 | +``` + +## Example Output (JSON) + +```json +{ + "scannedFiles": 2, + "totalUsages": 2, + "uniqueFlags": ["checkout-v2", "discount-percentage"], + "usages": [ + { + "flagKey": "checkout-v2", + "isDynamic": false, + "file": "checkout.go", + "line": 13, + "column": 16, + "callType": "BoolVariation", + "fingerprint": "launchdarkly:BoolVariation:checkout-v2:checkout.go", + "stalenessSignals": [], + "language": "go", + "sdk": "go-server-sdk-v7", + "risk": "low" + } + ], + "warnings": [] +} +``` + +Fingerprints (`launchdarkly:callType:flagKey:normalizedPath[:dynamicIndex]`) are stable across line-number changes and match flaglint-js's format byte-for-byte — see the [cross-tool contract](https://github.com/flaglint/flaglint-go/blob/main/docs/adr/003-cross-tool-contract.md). + +## Exit Behavior + +`scan` always exits `0` unless the tool itself errors (invalid directory, bad `--format`, unreadable config). It never fails based on what it finds — use `flaglint-go validate` for a CI gate. + +## Feedback + +- [Edit this page on GitHub](https://github.com/flaglint/flaglint.dev/edit/main/src/content/docs/docs/go/cli/scan.md) +- [Report an issue](https://github.com/flaglint/flaglint-go/issues/new) +- Next: [audit](/docs/go/cli/audit/) diff --git a/src/content/docs/docs/go/cli/validate.md b/src/content/docs/docs/go/cli/validate.md new file mode 100644 index 0000000..944e2fb --- /dev/null +++ b/src/content/docs/docs/go/cli/validate.md @@ -0,0 +1,99 @@ +--- +title: flaglint-go validate +description: Enforce a no-direct-LaunchDarkly policy, baseline-aware CI enforcement, and SARIF findings for Go. +lastUpdated: 2026-07-06 +--- + +`flaglint-go validate` checks whether a Go codebase complies with a flag-usage policy. It's the only flaglint-go command that ever exits `1`. + +## Options + +| Flag | Description | +| --- | --- | +| `--no-direct-launchdarkly` | Fail if any direct LaunchDarkly Go SDK evaluation calls are found | +| `--bootstrap-exclude stringArray` | Glob pattern for files allowed to use the LaunchDarkly SDK directly (repeatable) | +| `-f, --format string` | Output format: `text` \| `sarif` (default `text`) | +| `-o, --output string` | Write report to a file instead of stdout | +| `--config string` | Path to config file | +| `--baseline string` | Baseline file for comparing against known debt | +| `--fail-on-new` | Exit `1` if any findings are not in the baseline | + +## Blocking Policy Command + +```bash +flaglint-go validate ./services --no-direct-launchdarkly +``` + +```text +✗ validate --no-direct-launchdarkly: 2 direct LaunchDarkly Go SDK call(s) found. + + checkout.go:13:16 — BoolVariation("checkout-v2") + checkout.go:20:12 — IntVariation("discount-percentage") + +These call sites must migrate to OpenFeature before this rule passes. +``` + +Pass output once no direct calls remain: + +```text +✓ validate --no-direct-launchdarkly: no direct LaunchDarkly Go SDK calls found. + Scanned 2 file(s). +``` + +## Baseline Mode + +`--baseline`/`--fail-on-new` runs **independently** of `--no-direct-launchdarkly` — use it alone to adopt CI enforcement before existing debt is resolved: + +```bash +flaglint-go audit ./services --write-baseline .flaglint-baseline.json +flaglint-go validate ./services --baseline .flaglint-baseline.json --fail-on-new +``` + +```text +Scanned 2 file(s). Found 2 LaunchDarkly Go SDK usage(s). +✓ No new findings beyond baseline +``` + +Once new debt is introduced: + +```text +Scanned 2 file(s). Found 3 LaunchDarkly Go SDK usage(s). +Error: 1 new finding(s) not in baseline: + - launchdarkly:BoolVariation:new-feature-flag:checkout.go +``` + +Re-run `--write-baseline` whenever you accept new debt on purpose. + +## SARIF + +```bash +flaglint-go validate ./services --no-direct-launchdarkly --format sarif --output flaglint.sarif +``` + +SARIF findings use rule ID `flaglint.go.direct-launchdarkly` (Go-namespaced — distinct from flaglint-js's `flaglint.direct-launchdarkly`, per the [cross-tool contract](https://github.com/flaglint/flaglint-go/blob/main/docs/adr/003-cross-tool-contract.md)). Note: unlike flaglint-js's 0-based ESTree columns, Go's `token.Position.Column` is already 1-based, so flaglint-go does not add 1 to column numbers. + +## Bootstrap Exclusions + +Use `--bootstrap-exclude` for files allowed to use the SDK directly (a provider-wiring package, for example): + +```bash +flaglint-go validate ./services --no-direct-launchdarkly --bootstrap-exclude "internal/openfeature-bootstrap/**" +``` + +## Exit Codes + +| Code | Meaning | +| --- | --- | +| 0 | Success — no policy violations | +| 1 | Policy failure — direct calls found (`--no-direct-launchdarkly`) or new findings beyond baseline (`--fail-on-new`) | +| 2 | Invalid input — bad directory, bad `--format`, malformed config/baseline | +| 3 | Internal error | +| 130 | Interrupted (Ctrl-C) | + +`scan` and `audit` never exit anything but `0` (barring a tool error) — `validate` is the only policy gate. + +## Feedback + +- [Edit this page on GitHub](https://github.com/flaglint/flaglint.dev/edit/main/src/content/docs/docs/go/cli/validate.md) +- [Report an issue](https://github.com/flaglint/flaglint-go/issues/new) +- Next: [Identity Model](/docs/go/concepts/identity-model/) diff --git a/src/content/docs/docs/go/concepts/identity-model.md b/src/content/docs/docs/go/concepts/identity-model.md new file mode 100644 index 0000000..a5690e5 --- /dev/null +++ b/src/content/docs/docs/go/concepts/identity-model.md @@ -0,0 +1,83 @@ +--- +title: flaglint-go Identity Model +description: How flaglint-go proves a variable is a LaunchDarkly client — never by name, and across the whole scan, not just one file. +lastUpdated: 2026-07-06 +--- + +flaglint-go's core rule, inherited from flaglint-js: a variable is only treated as a LaunchDarkly client when its identity can be **proven** — never by matching a method or variable name in isolation. + +## Why not just search for method names? + +Most flag scanners look for calls like `.BoolVariation(...)` or `.variation(...)` anywhere in the source. This produces false positives the moment an unrelated type happens to define a same-named method: + +```go +// ❌ a name-only scanner would match this +type FakeClient struct{} +func (c *FakeClient) BoolVariation(key string, ctx interface{}, def bool) (bool, error) { + return def, nil +} + +// ✅ flaglint-go correctly ignores it — FakeClient is never traced back +// to a github.com/launchdarkly/go-server-sdk import +``` + +flaglint-go proves identity through **import-alias tracing plus constructor-call binding**: a variable is only a client if it traces back, through the AST, to `ld.MakeClient(...)`/`ld.MakeCustomClient(...)` for a package actually imported from `github.com/launchdarkly/go-server-sdk` (v6 or v7) — whatever local alias is used, including dot-imports. + +## Whole-scan resolution + +Real-world Go code rarely constructs and uses the client in the same place. flaglint-go parses every file in a scan up front and resolves several indirection patterns *across* files — not just within one: + +**Struct fields and composite literals** — the client stored into a wrapper struct: +```go +type Integration struct { + ldClient *ld.LDClient +} + +func setup() *Integration { + client, _ := ld.MakeClient("sdk-key", 5*time.Second) + return &Integration{ldClient: client} +} +``` + +**Multi-level field chains**, including through generics — a struct's fields can be declared in a different file (even a different file than where they're used): +```go +type FeatureFlag[T comparable] struct { + integ *Integration +} + +func (f *FeatureFlag[T]) Evaluate() bool { + v, _ := f.integ.ldClient.BoolVariation("my-flag", nil, false) + return v +} +``` + +**Cross-package factory/getter functions** — a package-level singleton getter called from a different package, resolved via real `go.mod`-derived import paths (never a name-based guess): +```go +// package flags +func GetLdClient() *ld.LDClient { /* ... */ } + +// package main +client := flags.GetLdClient() +client.BoolVariation("my-flag", nil, false) +``` + +**Parameter-typed bindings** — a client passed in as a plain parameter, with no assignment to trace at all: +```go +func useClient(client *ld.LDClient) { + client.BoolVariation("my-flag", nil, false) // bound from the declared type alone +} +``` + +## What stays out of scope + +Identity resolution is deliberately conservative — flaglint-go prefers a missed detection (false negative) over a false positive. Patterns not resolved without real type information (`go/types`) are documented, not silently guessed at. See [Limitations](/docs/go/reference/limitations/) for the current list. + +## Dynamic Key Detection + +A flag-key argument is `isDynamic: true` whenever it isn't a string literal — an identifier, a `fmt.Sprintf(...)` call, or string concatenation. Dynamic call sites are always classified high risk, regardless of the underlying method's own risk level, since the key "cannot resolve statically" no matter how simple the method otherwise is. + +## Feedback + +- [Edit this page on GitHub](https://github.com/flaglint/flaglint.dev/edit/main/src/content/docs/docs/go/concepts/identity-model.md) +- [Report an issue](https://github.com/flaglint/flaglint-go/issues/new) +- Next: [Supported Scope](/docs/go/reference/supported-scope/) diff --git a/src/content/docs/docs/go/index.md b/src/content/docs/docs/go/index.md new file mode 100644 index 0000000..d83acc2 --- /dev/null +++ b/src/content/docs/docs/go/index.md @@ -0,0 +1,54 @@ +--- +title: flaglint-go Overview +description: Audit LaunchDarkly Go server SDK usage with a native Go binary — no Node.js required. +lastUpdated: 2026-07-06 +tableOfContents: false +--- + +flaglint-go is the Go-native counterpart to flaglint-js — a standalone binary for auditing [LaunchDarkly Go server SDK](https://github.com/launchdarkly/go-server-sdk) usage, built for teams that don't want a Node.js dependency in their toolchain. + +```bash +brew install flaglint/tap/flaglint-go +flaglint-go audit ./services +``` + +No API key. No source upload. Runs locally against your checkout, using Go's own `go/parser`/`go/ast` — no build required, no `go/types` type-checking, no tree-sitter. + +
+ Quickstart + CLI Reference + Identity Model + Supported Scope +
+ +## flaglint-go vs. flaglint-js + +flaglint-go is an early-access, separate binary — not a Go mode bolted onto the npm CLI. They share the same fingerprint, exit-code, config, and SARIF conventions ([cross-tool contract](https://github.com/flaglint/flaglint-go/blob/main/docs/adr/003-cross-tool-contract.md)), but flaglint-go's command surface is currently narrower: + +| | flaglint-js | flaglint-go | +|---|---|---| +| `scan` — structured inventory | ✅ | ✅ | +| `audit` — risk-ranked report + readiness score | ✅ | ✅ | +| `validate` — CI policy gate | ✅ | ✅ | +| `migrate` — automated OpenFeature rewrites | ✅ | ❌ not yet | +| `init` — scaffold a config file | ✅ | ❌ not yet | +| `completion` — shell autocompletion scripts | ✅ | ✅ (Cobra's stock boilerplate — bash/zsh/fish/PowerShell) | + +## What flaglint-go Does + +- Performs local, syntax-only source analysis — no build, no `go/types`, no network access. +- Detects `github.com/launchdarkly/go-server-sdk` v6 and v7 evaluation calls, proven through import-alias tracing and constructor-call binding — never through name matching alone. +- Resolves common real-world indirection across an entire scan (not just one file): struct fields, composite literals, multi-level field chains, cross-package factory/getter functions, and typed parameters. See [Identity Model](/docs/go/concepts/identity-model/). +- Generates the same inventory/audit/validate reports as flaglint-js (JSON, Markdown, text, SARIF). + +## What flaglint-go Does Not Do + +- It does not rewrite code. There is no `migrate`/`--apply` command yet. +- It does not resolve every indirection pattern — method values, interface satisfaction, and a few other gaps are documented and tracked. See [Limitations](/docs/go/reference/limitations/). +- It does not query LaunchDarkly for flag age, owner, evaluation history, or production usage. + +## Feedback + +- [Edit this page on GitHub](https://github.com/flaglint/flaglint.dev/edit/main/src/content/docs/docs/go/index.md) +- [Report an issue](https://github.com/flaglint/flaglint-go/issues/new) +- Next: [Quickstart](/docs/go/quickstart/) diff --git a/src/content/docs/docs/go/quickstart.md b/src/content/docs/docs/go/quickstart.md new file mode 100644 index 0000000..7bcbaac --- /dev/null +++ b/src/content/docs/docs/go/quickstart.md @@ -0,0 +1,129 @@ +--- +title: flaglint-go Quickstart +description: Install flaglint-go, run your first audit, and enforce a CI policy — no Node.js required. +lastUpdated: 2026-07-06 +--- + +## Installation + +**Homebrew (macOS / Linux)** +```bash +brew install flaglint/tap/flaglint-go +``` + +**go install** +```bash +go install github.com/flaglint/flaglint-go/cmd/flaglint-go@latest +``` + +Prebuilt release binaries are also available from the [releases page](https://github.com/flaglint/flaglint-go/releases). + +## Requirements + +- A Go project using `github.com/launchdarkly/go-server-sdk` v6 or v7. +- Nothing else — flaglint-go is a single static binary with no runtime dependencies. It does not need your project to build; it parses source with `go/parser` only. + +## 1. Run an Audit + +```bash +flaglint-go audit ./services +``` + +Example output from a small service wiring the client through a package-level singleton getter (a pattern flaglint-go resolves across files — see [Identity Model](/docs/go/concepts/identity-model/)): + +```go +// pkg/flags/client.go +var client *ld.LDClient + +func Client() *ld.LDClient { return client } +``` + +```go +// checkout.go +client := flags.Client() +enabled, _ := client.BoolVariation("checkout-v2", ctx, false) +pct, _ := client.IntVariation("discount-percentage", ctx, 0) +``` + +```text +Scan complete — 2 unique flag(s) across 2 call site(s) (0ms, 2 file(s)) +Migration readiness: 100/100 · ready + 2 low risk · 0 medium risk · 0 high risk +``` + +**The three commands and when to use each:** + +- `audit` — risk-ranked overview with a migration-readiness score. Start here. +- `scan` — structured, file-level inventory (JSON/Markdown) for automation or deeper review. +- `validate` — CI gate that blocks new direct LaunchDarkly evaluation calls. + +## 2. Inspect Detailed Inventory With Scan + +```bash +flaglint-go scan ./services --format json +``` + +```json +{ + "scannedFiles": 2, + "totalUsages": 2, + "uniqueFlags": ["checkout-v2", "discount-percentage"], + "usages": [ + { + "flagKey": "checkout-v2", + "isDynamic": false, + "file": "checkout.go", + "line": 13, + "callType": "BoolVariation", + "fingerprint": "launchdarkly:BoolVariation:checkout-v2:checkout.go", + "risk": "low" + } + ] +} +``` + +## 3. Enforce a Policy in CI + +```bash +flaglint-go validate ./services --no-direct-launchdarkly +``` + +```text +✗ validate --no-direct-launchdarkly: 2 direct LaunchDarkly Go SDK call(s) found. + + checkout.go:13:16 — BoolVariation("checkout-v2") + checkout.go:20:12 — IntVariation("discount-percentage") + +These call sites must migrate to OpenFeature before this rule passes. +``` + +`validate` is the only command that ever exits `1` — `scan` and `audit` always exit `0` unless something breaks (a bad directory, malformed config). + +## 4. Adopt Gradually With a Baseline + +Turning on `--no-direct-launchdarkly` immediately fails CI for every existing call site. To adopt flaglint-go without a big-bang migration, capture current debt as a baseline and only fail on *new* debt: + +```bash +# Capture current findings once, commit the file +flaglint-go audit ./services --write-baseline .flaglint-baseline.json + +# In CI: pass as long as no new debt was introduced +flaglint-go validate ./services --baseline .flaglint-baseline.json --fail-on-new +``` + +```text +Scanned 2 file(s). Found 2 LaunchDarkly Go SDK usage(s). +✓ No new findings beyond baseline +``` + +`--baseline`/`--fail-on-new` runs independently of `--no-direct-launchdarkly` — most teams adopt with baseline mode alone, then turn on the strict policy later once existing debt is cleared. + +--- + +**Further reading:** [CLI reference](/docs/go/cli/audit/) · [Identity Model](/docs/go/concepts/identity-model/) · [Enforce in CI guide](/docs/go/guides/enforce-in-ci/) + +## Feedback + +- [Edit this page on GitHub](https://github.com/flaglint/flaglint.dev/edit/main/src/content/docs/docs/go/quickstart.md) +- [Report an issue](https://github.com/flaglint/flaglint-go/issues/new) +- Next: [CLI Reference](/docs/go/cli/audit/) diff --git a/src/content/docs/docs/go/reference/limitations.md b/src/content/docs/docs/go/reference/limitations.md new file mode 100644 index 0000000..a77af89 --- /dev/null +++ b/src/content/docs/docs/go/reference/limitations.md @@ -0,0 +1,40 @@ +--- +title: flaglint-go Limitations +description: Current boundaries and non-goals of flaglint-go. +lastUpdated: 2026-07-06 +--- + +flaglint-go is intentionally narrow and honest about what it can't yet prove. + +## No Automated Migration + +There is no `migrate`/`--apply` command. flaglint-go audits and enforces policy; it does not rewrite source. If you need automated OpenFeature rewrites today, that capability currently exists only in [flaglint-js](/docs/quickstart/) for Node.js/TypeScript. + +## Identity Resolution Gaps + +flaglint-go proves client identity from syntax alone — no build, no `go/types`. That means a few real patterns aren't resolved yet, all filed as tracked, documented gaps rather than silently guessed at: + +- **Chained factory-call-then-method** (`pkg.GetLdClient().Method(...)` with no intermediate variable) — [issue #20](https://github.com/flaglint/flaglint-go/issues/20) +- **Method values** (`f := client.BoolVariation; f(...)`) — [issue #6](https://github.com/flaglint/flaglint-go/issues/6) +- **Interface satisfaction** (a client known only through an interface type) — [issue #15](https://github.com/flaglint/flaglint-go/issues/15) +- **Block-scoped variable shadowing** within a single function — [issue #5](https://github.com/flaglint/flaglint-go/issues/5). Unlike every other gap below, this one **can cause a false positive**: a variable re-`:=`'d to an unrelated value inside a nested block is still treated as the outer real client. +- **A factory function returning a wrapper type**, not `*ld.LDClient` itself — [issue #16](https://github.com/flaglint/flaglint-go/issues/16) +- **Nested `go.mod` files** within one scanned tree (monorepo submodules) — [issue #17](https://github.com/flaglint/flaglint-go/issues/17) + +Every gap above fails safe (a missed detection, never a false positive) except block-scoped shadowing, noted above. See [Identity Model](/docs/go/concepts/identity-model/) and [ADR 004](https://github.com/flaglint/flaglint-go/blob/main/docs/adr/004-whole-scan-identity-resolution.md) for the full design and reasoning behind each. + +## Not Production Staleness Analysis + +flaglint-go does not query LaunchDarkly and does not identify production-stale flags. It cannot know flag age, owner, evaluation history, environment configuration, or production usage from source alone. + +## Outside Detection Coverage + +- Browser/mobile LaunchDarkly SDKs. +- Non-LaunchDarkly feature-flag providers. +- Runtime-only flag key construction that can't be resolved statically (reported as high-risk "dynamic", not silently ignored). + +## Feedback + +- [Edit this page on GitHub](https://github.com/flaglint/flaglint.dev/edit/main/src/content/docs/docs/go/reference/limitations.md) +- [Report an issue](https://github.com/flaglint/flaglint-go/issues/new) +- Next: [flaglint-go Overview](/docs/go/) diff --git a/src/content/docs/docs/go/reference/supported-scope.md b/src/content/docs/docs/go/reference/supported-scope.md new file mode 100644 index 0000000..54d9473 --- /dev/null +++ b/src/content/docs/docs/go/reference/supported-scope.md @@ -0,0 +1,52 @@ +--- +title: flaglint-go Supported Scope +description: What flaglint-go detects, reports, and excludes. +lastUpdated: 2026-07-06 +--- + +This page covers **flaglint-go**, the Go CLI. For the JavaScript/TypeScript CLI's scope, see [flaglint-js: Supported Scope](/docs/reference/supported-scope/). + +flaglint-go detects LaunchDarkly Go server-side SDK evaluation calls from: + +- `github.com/launchdarkly/go-server-sdk/v6` +- `github.com/launchdarkly/go-server-sdk/v7` + +## API Coverage + +| Method | Detected | Risk | +| --- | --- | --- | +| `BoolVariation` / `StringVariation` / `IntVariation` / `Float64Variation` | Yes | Low (static key) | +| `*Ctx` forms of the above | Yes | Low (static key) | +| `JSONVariation` (and `*Ctx`) | Yes | Medium | +| `*VariationDetail(Ctx)` methods | Yes | High | +| `AllFlagsState` | Yes | High | +| Dynamic flag keys | Yes | High (overrides the method's own risk) | +| Browser/mobile LaunchDarkly SDKs | No | — | +| Non-LaunchDarkly providers | No | — | + +## Identity Resolution Coverage + +flaglint-go proves client identity syntactically (no `go/types`, no build required) across an entire scan. See [Identity Model](/docs/go/concepts/identity-model/) for detail on each of these: + +| Pattern | Resolved | +| --- | --- | +| Direct constructor binding (`x := ld.MakeClient(...)`) | Yes | +| Package-level `var` and struct-field assignment | Yes — across the whole scan, not just one file | +| Composite-literal struct-field binding (`&Integration{ldClient: client}`) | Yes | +| Multi-level field-selector chains (`f.integ.ldClient.Method(...)`), including generic structs | Yes | +| Cross-package factory/getter functions (`pkg.GetLdClient()`) | Yes — requires a `go.mod` to compute real import paths | +| Parameter-typed client bindings (`func f(client *ld.LDClient)`) | Yes | +| Chained factory-call-then-method (`pkg.GetLdClient().Method(...)`, no intermediate variable) | **No** — [tracked](https://github.com/flaglint/flaglint-go/issues/20) | +| Method values (`f := client.BoolVariation; f(...)`) | No — [tracked](https://github.com/flaglint/flaglint-go/issues/6) | +| Interface satisfaction (client known only through an interface type) | No — [tracked](https://github.com/flaglint/flaglint-go/issues/15) | +| Block-scoped variable shadowing within one function | No — can cause a false positive, see below — [tracked](https://github.com/flaglint/flaglint-go/issues/5) | +| A factory function returning a wrapper type (not `*ld.LDClient` itself) | No — [tracked](https://github.com/flaglint/flaglint-go/issues/16) | +| Nested `go.mod` files within one scanned tree (monorepo submodules) | Partial — [tracked](https://github.com/flaglint/flaglint-go/issues/17) | + +Every "No" above is a false-negative risk except one: block-scoped shadowing (issue #5) can cause a genuine false positive — a variable re-`:=`'d to an unrelated value inside a nested block is still treated as the outer real client. Every other gap fails safe (a missed detection, never a false positive) — flaglint-go's non-negotiable rule is to under-detect rather than guess everywhere else. See [Limitations](/docs/go/reference/limitations/). + +## Feedback + +- [Edit this page on GitHub](https://github.com/flaglint/flaglint.dev/edit/main/src/content/docs/docs/go/reference/supported-scope.md) +- [Report an issue](https://github.com/flaglint/flaglint-go/issues/new) +- Next: [Limitations](/docs/go/reference/limitations/) diff --git a/src/content/docs/docs/reference/changelog.md b/src/content/docs/docs/reference/changelog.md index 180bbe3..fa1b1da 100644 --- a/src/content/docs/docs/reference/changelog.md +++ b/src/content/docs/docs/reference/changelog.md @@ -75,8 +75,12 @@ v1.0.0 is the CI-readiness release. The core scanner and migrator are stable. Th - **ADR 005 — Cleanup command** (DEFERRED) — documents the safe path for automated branch removal via `--assume`/`--flag-values`; deferred until `--assume` mechanism is designed. -- **ADR 006 — Go language support** (PROPOSED) — tree-sitter-go parser design, - import tracing for `go-server-sdk`, go.mod boundary handling, 6-phase plan. +- **ADR 006 — Go language support** (PROPOSED, later superseded) — + tree-sitter-go parser design, import tracing for `go-server-sdk`, go.mod + boundary handling, 6-phase plan. Go support ultimately shipped as a + separate project, [flaglint-go](https://github.com/flaglint/flaglint-go), + built around Go's native `go/parser`/`go/ast` instead of tree-sitter — see + [flaglint-go's docs](/docs/go/). ### Fixed diff --git a/src/content/docs/docs/reference/supported-scope.md b/src/content/docs/docs/reference/supported-scope.md index f905014..f9b0f1b 100644 --- a/src/content/docs/docs/reference/supported-scope.md +++ b/src/content/docs/docs/reference/supported-scope.md @@ -1,9 +1,11 @@ --- title: Supported Scope description: What FlagLint detects, migrates, reports for manual review, and excludes. -lastUpdated: 2026-07-01 +lastUpdated: 2026-07-06 --- +This page covers **flaglint-js**, the JavaScript/TypeScript CLI. For the Go CLI's supported scope, see [flaglint-go: Supported Scope](/docs/go/reference/supported-scope/). + FlagLint currently supports LaunchDarkly Node.js server-side SDK evaluation calls in JavaScript and TypeScript from: - `@launchdarkly/node-server-sdk` @@ -30,7 +32,8 @@ React SDK hooks, HOC, and provider are detected and reported for manual review. | React SDK hooks (`useFlags`, `useLDClient`, `useVariation`) | Yes | No | Yes | No | | React SDK HOC (`withLDConsumer`) | Yes | No | Yes | No | | React SDK provider (`LDProvider`, `asyncWithLDProvider`) | Yes | No | Yes | No | -| Go, Java, Python, or other SDKs | No | No | No | Yes | +| Go SDK | Handled by the separate [flaglint-go](/docs/go/) CLI | — | — | — | +| Java, Python, or other SDKs | No | No | No | Yes | | Non-LaunchDarkly providers | No | No | No | Yes | ## Supported Import Provenance