Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# AGENTS.md

This repository's guidelines for AI coding agents live in [CLAUDE.md](CLAUDE.md), the single source of truth.

Read **@CLAUDE.md** for the project overview and working conventions. Detailed material lives under [`references/`](references/), linked from there.
147 changes: 147 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# AI Agent Guidelines for auth0-cli

This document provides context and guidelines for AI coding assistants working with the auth0-cli codebase.

## Your Role

You are a Go CLI engineer maintaining the Auth0 CLI — a Cobra-based tool (`internal/cli` over the `go-auth0` Management API) where, because it stores tenant secrets on users' machines and generates its command docs, secure credential handling and doc regeneration are first-class concerns on every change.

---

## Working Principles

Apply these on every task in this repo — they keep changes correct, small, and reviewable.

- **Think before coding.** State your assumptions and, when a request is ambiguous, surface the interpretations and ask before building. Recommend a simpler approach when you see one. A clarifying question up front beats a wrong implementation.
- **Simplicity first.** Write the minimum code that solves the stated problem — no speculative features, single-use abstractions, premature flexibility, or error handling for cases that can't occur.
- **Surgical changes.** Touch only what the request requires. Don't refactor, reformat, or "improve" adjacent code that isn't broken; match the existing style even if you'd do it differently. Every changed line should trace directly to the request. Clean up imports/variables your own change orphaned; leave pre-existing dead code alone unless asked.
- **Goal-driven execution.** Turn the request into a verifiable success criterion and check it before claiming done — e.g. "add a flag" becomes "add the flag, wire it through, add a table-driven test, and regenerate docs." Don't report success you haven't verified.

---

## Project Overview

**auth0-cli** is the official command-line interface for Auth0 — build, manage, and test Auth0 integrations from the terminal.

- **Language:** Go 1.25.8
- **Tech Stack:** Cobra (commands) + pflag, go-auth0 Management SDK (v1 `management` and v2), Sentry crash reporting, zalando/go-keyring for secret storage, terraform-exec (Terraform export), charmbracelet/glamour (markdown rendering)
- **Package Manager:** Go modules — **vendored** (`vendor/` is committed; run `go mod tidy && go mod vendor` after dependency changes)
- **Minimum Platform Version:** Go 1.25.8 (from `go.mod`)
- **Dependencies:** go-auth0 v1.44.0 + v2.14.0, spf13/cobra 1.10.2, getsentry/sentry-go 0.47.0, zalando/go-keyring 0.2.8 · test: stretchr/testify 1.11.1, golang/mock (gomock) 1.6.0

---

## Project Structure

```
auth0-cli/
├── cmd/
│ ├── auth0/ # Main entrypoint — calls cli.Execute()
│ └── doc-gen/ # Generates docs/*.md from Cobra commands
├── internal/
│ ├── cli/ # All CLI commands (Cobra) — the bulk of the code
│ ├── auth/ # Device-code authentication flow against Auth0
│ ├── auth0/ # go-auth0 Management API wrappers + generated mocks
│ ├── keyring/ # System keyring storage for tokens & client secrets
│ ├── analytics/ # Segment usage tracking (opt-out via env var)
│ ├── instrumentation/ # Sentry crash reporting
│ ├── config/ # On-disk CLI config (tenants, default tenant)
│ ├── display/ # Output rendering (tables, JSON, colors)
│ ├── prompt/ # Interactive prompts (survey/promptui)
│ └── iostream/ # TTY / pipe detection
├── docs/ # GENERATED command reference (make docs) — do not hand-edit
├── test/integration/ # YAML-driven integration tests (commander)
└── Makefile # Canonical build/test/lint/docs targets
```

### Key Files

| File | Purpose |
|------|---------|
| `cmd/auth0/main.go` | Entry point — thin wrapper over `cli.Execute()` |
| `internal/cli/root.go` | Root command, DI wiring (`cli` struct, renderer, tracker) |
| `internal/cli/cli.go` | `cli` struct, tenant/config setup, API client init |
| `internal/auth/auth.go` | Device-code OAuth flow, token exchange |
| `internal/keyring/keyring.go` | Secret storage abstraction over go-keyring |
| `Makefile` | All build/test/lint/docs commands |

---

## Boundaries

### ✅ Always Do

- Run `make lint` and `make test-unit` before committing.
- Follow the existing Cobra command patterns and naming (see [references/code-style.md](references/code-style.md)).
- Add table-driven unit tests for new functionality; regenerate mocks with `make test-mocks` when an interface changes.
- **Regenerate command docs with `make docs` whenever you add/change a command, flag, or help text.** CI runs `make check-docs` and fails if `docs/` is out of sync.
- Update `README.md` in the same PR when a change touches what it documents — installation, config/auth, the top-level command list, deprecations, or supported workflows (per-flag and per-command detail lives in the generated `docs/`, via `make docs`, not the README). Update `CUSTOMIZATION_GUIDE.md` for Universal Login/branding changes and `MIGRATION_GUIDE.md` for breaking changes.
- After changing dependencies, run `go mod tidy && go mod vendor` — the `vendor/` directory is committed and must stay in sync.
- Route new usage tracking through the existing `analytics.Tracker` (`internal/analytics`) and preserve the `AUTH0_CLI_ANALYTICS=false` opt-out; do not hand-roll a new tracking client.

### ⚠️ Ask First

- **Any breaking change to a command, flag, or output format — always ask first.** Never break backward compatibility on your own initiative.
- Adding new dependencies (also requires `go mod vendor`).
- Modifying authentication, token exchange, or keyring storage code (`internal/auth`, `internal/keyring`).
- Changes to CI/CD configuration (`.github/workflows/`, `.goreleaser.yml`).
- Running integration tests (`make test-integration`) — they hit a **live Auth0 tenant**, are slow, and can mutate real resources (see [references/testing.md](references/testing.md)).

### 🚫 Never Do

- Commit secrets, API keys, tokens, or a populated `.env`.
- Log or print access tokens, refresh tokens, or client secrets.
- Hand-edit generated files: `docs/*.md` (regenerate via `make docs`) or `internal/auth0/mock/*` (regenerate via `make test-mocks`).
- Hand-edit the `vendor/` directory.
- Remove or skip failing tests without fixing them.
- Break backward compatibility without asking first and getting explicit approval.

---

## Security Considerations

- **Credential storage:** Client secrets, access tokens, and legacy refresh tokens are stored in the OS keyring via `zalando/go-keyring` (`internal/keyring`). Access tokens are chunked (2048-byte segments) because some keyrings cap value size. Never move secrets to plaintext config or logs.
- **Authentication:** Uses the OAuth device-authorization flow (`internal/auth`) for interactive login, and client-credentials (secret or private-key JWT) for machine auth. Do not weaken or bypass these flows.
- **Crash reporting:** `internal/instrumentation` ships a **public, write-only** Sentry DSN (safe to embed). Crash reporting is disabled for `dev`/empty-version builds — do not enable it for local builds.
- **Analytics:** `internal/analytics` sends usage events; honor the `AUTH0_CLI_ANALYTICS=false` opt-out and the debug-build skip.
- **Never commit secrets, API keys, or tokens.**

---

> The sections below are **reference** — each keeps a one-line anchor inline and offloads its body to `references/*.md`. Read a file only when the task needs it.

## Commands

Core loop: `make build` (binary to `./out/auth0`), `make test-unit` (safe, no creds), `make lint`, `make docs` (regenerate command reference).

See [references/commands.md](references/commands.md) for the full command list. Read it when you need to build, test, lint, generate docs/mocks, or check vulnerabilities.

## Testing

Framework is Go's `testing` + `testify` assertions + `gomock`; tests are table-driven and colocated as `*_test.go`. The default `make test-unit` suite is unit-only and needs no credentials; `make test-integration` hits a live tenant and requires `AUTH0_DOMAIN`/`AUTH0_CLIENT_ID`/`AUTH0_CLIENT_SECRET` (Ask First).

See [references/testing.md](references/testing.md) for conventions, mocking, running a single test, and the integration tier. Read it when writing or running tests.

## Code Style

Go standard style enforced by `golangci-lint` (v2): `gofmt -s` + `goimports` with local prefix `github.com/auth0/auth0-cli`, plus `errcheck`, `revive`, `staticcheck`, `gocritic`, `godot` (comments end with a capitalized sentence + period). Commands follow a consistent Cobra constructor pattern with declarative `Flag` structs.

See [references/code-style.md](references/code-style.md) for naming, the command pattern, and good/bad examples. Read it when adding or editing a command.

## Git Workflow

Branch names are ticket-scoped (e.g. `DXCDT-1234/short-description`) or `docs/…`, `fix-…`. PRs use `.github/PULL_REQUEST_TEMPLATE.md` (Changes / References / Testing sections).

See [references/git-workflow.md](references/git-workflow.md) for branch, commit, and PR conventions. Read it before committing or opening a PR.

## Common Pitfalls

The top one: forgetting `make docs` after a command/flag change fails CI (`make check-docs`). Others involve vendoring, mock regeneration, and the v1/v2 go-auth0 split.

See [references/pitfalls.md](references/pitfalls.md) for the full list. Read it when a build/CI step fails unexpectedly.

## Docs Update Rules

The `docs/` command reference is **generated** — never hand-edit it; run `make docs`. Prose docs (`README.md`, guides) are hand-maintained.

See [references/docs-update.md](references/docs-update.md) for the tracked-docs inventory and the code-to-docs mapping. Read it when your change touches user-facing behavior.
79 changes: 79 additions & 0 deletions references/code-style.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Code Style

## Enforced tooling

`golangci-lint` v2 (`.golangci.yml`) is the gate. Enabled linters: `errcheck`, `gocritic`, `godot`, `revive`, `staticcheck` (all checks), `unconvert`, `unused`, `whitespace`. Formatters: `gofmt` with `simplify`, and `goimports` with local prefix `github.com/auth0/auth0-cli` (local imports grouped last).

- `godot`: comments must be full sentences — capitalized, ending in a period.
- `errcheck`: check returned errors (the config exempts some via exclusion rules, but prefer handling them).

## Naming conventions

- Standard Go: `PascalCase` for exported identifiers, `camelCase` for unexported, short receiver names.
- Command files are named after the resource: `apps.go`, `apis.go`, `custom_domains.go`; their tests are `<name>_test.go`.
- Flags are declared as package-level `Flag` structs (see below), named `<command><Field>` (e.g. `loginClientID`).

## The command pattern

Commands are Cobra constructors that take the shared `*cli` struct and return a `*cobra.Command`. Flags are declared declaratively:

**✅ Good** — declarative flag, wired into a Cobra command:

```go
var loginClientID = Flag{
Name: "Client ID",
LongForm: "client-id",
Help: "Client ID of the application when authenticating via client credentials.",
IsRequired: false,
}

func loginCmd(cli *cli) *cobra.Command {
var inputs LoginInputs
cmd := &cobra.Command{
Use: "login",
Short: "Authenticate to your tenant",
RunE: func(cmd *cobra.Command, args []string) error {
return runLogin(cmd.Context(), cli, &inputs)
},
}
loginClientID.RegisterString(cmd, &inputs.ClientID, "")
return cmd
}
```

**❌ Bad** — hardcoded flag strings, ignored error, no help text:

```go
func loginCmd(cli *cli) *cobra.Command {
cmd := &cobra.Command{Use: "login", Run: func(cmd *cobra.Command, args []string) {
id, _ := cmd.Flags().GetString("client-id") // errcheck: unchecked error
doLogin(id) // no context, no error return
}}
cmd.Flags().String("client-id", "", "") // no help; not a Flag struct
return cmd
}
```

## Dominant patterns

- **Dependency injection via the `cli` struct** (`internal/cli/cli.go`) — carries the renderer, analytics tracker, config, and API client; passed to every command constructor.
- **`RunE` returning errors** rather than `Run` + `os.Exit`; errors bubble to the root command.
- **Rendering through `internal/display`** — never `fmt.Println` results directly; use the renderer so JSON/table/format flags work.

## Machine-readable output

Every result-producing command supports mutually-exclusive output flags (`--json`, `--json-compact`, `--csv`) via the shared `cli` struct — wire them the same way existing commands do (see `roles.go`, `users.go`):

```go
cmd.Flags().BoolVar(&cli.jsonCompact, "json-compact", false, "Output in compact json format.")
cmd.MarkFlagsMutuallyExclusive("json", "json-compact", "csv")
```

`--json-compact` emits single-line JSON, ideal for piping to `jq` in scripts and command examples:

```bash
auth0 apps list --json-compact | jq '.[] | {client_id, name}'
auth0 users show <user-id> --json-compact | jq '{id: .user_id, email: .email}'
```

Prefer `--json-compact | jq ...` over hand-parsing table output when documenting or scripting against the CLI.
59 changes: 59 additions & 0 deletions references/commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Commands

All commands are Makefile targets (run `make help` to list them). These mirror what CI runs in `.github/workflows/main.yml`.

```bash
# Build the CLI binary for the native platform -> ./out/auth0
make build

# Install the binary into $GOPATH/bin
make install

# Build for all supported platforms (CI "Build" job)
make build-all-platforms

# Run unit tests (safe — no credentials required)
make test-unit

# Run all tests (unit + integration; integration needs a live tenant)
make test

# Run integration tests only (requires AUTH0_DOMAIN/CLIENT_ID/CLIENT_SECRET)
make test-integration
# Filter to a subset:
make test-integration FILTER="attack protection"

# Regenerate gomock mocks (after changing a mocked interface)
make test-mocks

# Lint (golangci-lint v2, config .golangci.yml)
make lint

# Check for known vulnerabilities (govulncheck)
make check-vuln

# Regenerate the docs/ command reference from Cobra commands
make docs

# Verify docs are in sync (CI gate — fails if `make docs` produces a diff)
make check-docs

# Download dependencies
make deps

# Clean the docs output
make docs-clean
```

## CI jobs (`.github/workflows/main.yml`)

1. **Checks** — `make check-docs` + `golangci-lint` with `-c .golangci.yml`.
2. **Unit Tests** — `make test-unit`, uploads coverage.
3. **Integration Tests** — `make test-integration` (skipped for forks/dependabot; only on `main`-targeted PRs). Uses `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, `AUTH0_CLIENT_SECRET` secrets.
4. **Build** — `make build-all-platforms`.

## Running without building

```bash
go run ./cmd/auth0 <command>
```
28 changes: 28 additions & 0 deletions references/docs-update.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Docs Update Rules

This is a **CLI** repo, so the code-to-docs mapping is command/flag-oriented.

## Tracked docs

| Doc | Covers | Present |
|-----|--------|---------|
| `README.md` | Install, quickstart, top-level command overview | present |
| `docs/*.md` | **Generated** per-command reference (`make docs`) — one file per command | present |
| `CUSTOMIZATION_GUIDE.md` | Universal Login / branding customization workflow | present |
| `MIGRATION_GUIDE.md` | Migration notes for breaking changes | present |
| `CONTRIBUTING.md` | Dev setup, adding a command, adding a dependency, releasing | present |

> `EXAMPLES.md` is not tracked in this repo — usage examples live in `README.md` and the generated `docs/`.

## When you change code, update these docs

| Change | Update |
|--------|--------|
| Add/rename/remove a command | `make docs` (regenerates `docs/`), plus `README.md` if it's a top-level command |
| Add/change a flag or its help text | `make docs` |
| Change command output/behavior | `make docs` if help text changed |
| Breaking change (flag/output/command removed or renamed) | Ask first; then `MIGRATION_GUIDE.md` + `make docs` |
| Change to Universal Login / branding flow | `CUSTOMIZATION_GUIDE.md` |
| Change dev setup, build, or release steps | `CONTRIBUTING.md` |

> The generated `docs/` reference must never be hand-edited — always regenerate via `make docs`. CI's `make check-docs` enforces this. Update the mapped hand-written doc **in the same PR** as the code change.
34 changes: 34 additions & 0 deletions references/git-workflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Git Workflow

## Branch naming

Observed conventions in this repo:

- Ticket-scoped: `DXCDT-1234/short-description` (Jira key + slug).
- Type-scoped: `docs/…`, `fix-…`, `issue-<number>-…`.
- Automated: `dependabot/…`.

Match the pattern that fits your change; prefer the ticket-scoped form when a Jira ticket exists.

## Commit messages

Conventional-commit style prefixes are used across history: `docs:`, `chore(deps):`, `fix:`, `feat:`. Keep the subject imperative and concise; scope in parentheses where useful (e.g. `chore(deps): bump ...`).

## Pull requests

Use `.github/PULL_REQUEST_TEMPLATE.md`, which has three sections:

- **🔧 Changes** — what changed and why; types/methods added, deleted, deprecated, or changed; usage summary for new/changed public surface.
- **📚 References** — GitHub issue/PR links, Community posts, related PRs.
- **🔬 Testing** — how the change was tested.

## Before opening a PR

1. `make lint`
2. `make test-unit`
3. `make docs` (if you touched commands/flags/help) — CI's `make check-docs` will fail otherwise.
4. `go mod tidy && go mod vendor` (if you touched dependencies).

## Releases

Releases are cut by maintainers via a tag-triggered GitHub workflow + Goreleaser — not by agents editing files. The `CHANGELOG.md` is written as part of that release flow (see the `Add changelog for vX.Y.Z` PRs), not by feature PRs. Do not bump versions or add changelog entries by hand as part of a feature change.
17 changes: 17 additions & 0 deletions references/pitfalls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Common Pitfalls

1. **Forgetting to regenerate docs.** Any change to a command, flag, or help string must be followed by `make docs`. CI runs `make check-docs`, which regenerates and fails if `git status` is dirty. This is the most common CI failure.

2. **Vendoring drift.** Dependencies are vendored (`vendor/` is committed). After `go get`/`go mod` changes you must run `go mod tidy && go mod vendor`, or the build/CI breaks. Do not hand-edit `vendor/`.

3. **Stale mocks.** Mocks in `internal/auth0/mock` are generated by `mockgen`. After changing a mocked interface, run `make test-mocks` — do not hand-edit the generated files.

4. **Two go-auth0 major versions coexist.** The repo imports both `github.com/auth0/go-auth0` (v1, `management`) and `github.com/auth0/go-auth0/v2`. Check which version a given command already uses before adding calls; don't mix types across the two.

5. **Printing results directly.** Use the `internal/display` renderer instead of `fmt.Println` so `--json` and format flags keep working, and so nothing accidentally prints a secret.

6. **Leaking secrets in output/logs.** Client secrets and tokens live in the OS keyring (`internal/keyring`). Never log them; secret-revealing output (e.g. `--reveal-secrets`) must be explicit and opt-in.

7. **Enabling telemetry/crash reporting for dev builds.** Both analytics and Sentry intentionally no-op when `buildinfo.Version` is empty or `dev`. Don't remove those guards.

8. **`godot` lint failures.** Comments must be complete sentences ending in a period — an easy lint miss on new code.
Loading
Loading