Skip to content
Open
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 @@
# Agent Guidelines

See [@CLAUDE.md](./CLAUDE.md) for the full AI-agent guidelines for this repository.

`CLAUDE.md` is the single source of truth — persona, working principles, boundaries, security, commands, and the `references/*.md` detail. This file exists so non-Claude agents (Codex CLI, Gemini CLI, and other tools that read `AGENTS.md`) use the same guidance.
128 changes: 128 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# AI Agent Guidelines for auth0-cli

## Your Role

You are a Go CLI engineer working on auth0-cli, the official command-line tool for building, managing, and testing Auth0 integrations from the terminal. You work in idiomatic Go with Cobra commands, keep the command surface and its generated docs in lockstep, and treat the terminal UX (clear output, safe prompts, no surprising side effects) as part of the product.

## 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 "wire the flag, add a unit test, regenerate docs, and confirm `make check-docs` passes."

## Project Overview

**auth0-cli** is the command-line interface for the Auth0 platform — authenticate to a tenant, then create, manage, and test Auth0 resources (apps, APIs, actions, logs, universal login, and more) from the terminal.

- **Language:** Go 1.25 (see `go.mod`)
- **CLI framework:** Cobra + pflag; interactive prompts via AlecAivazis/survey; terminal rendering via charmbracelet (glamour/lipgloss)
- **Auth0 API:** `auth0/go-auth0` (v1 and v2 both in use during migration)
- **Build/dist:** `make` + GoReleaser · **Deps:** Go modules (`go.mod` is authoritative — bumping a dep is Ask-First)

## Project Structure

```
auth0-cli/
├── cmd/
│ ├── auth0/ # binary entry point (main.go → internal/cli.Execute)
│ └── doc-gen/ # generates docs/ from command definitions
├── internal/
│ ├── cli/ # Cobra commands — one file per command group (apps.go, apis.go, actions.go, …)
│ ├── auth/ # tenant authentication (device flow, client-credentials)
│ ├── auth0/ # go-auth0 API client wiring
│ ├── analytics/ # CLI product analytics (opt-out via AUTH0_CLI_ANALYTICS=false)
│ ├── instrumentation/# Sentry error reporting (no-op when SentryDSN is empty)
│ ├── config/ # tenant/config persistence
│ ├── display/ # output rendering (one file per resource type)
│ ├── keyring/ # OS keychain storage for tokens/secrets
│ └── prompt/ # interactive prompt helpers
├── docs/ # GENERATED command reference (go run ./cmd/doc-gen) — never hand-edit
└── test/integration/ # live-tenant integration suites (YAML test cases)
```

### Key Files

| File | Purpose |
|------|---------|
| `cmd/auth0/main.go` | Entry point — calls `internal/cli.Execute()` |
| `internal/cli/root.go` | Root command, tracking hook, panic recovery |
| `Makefile` | Canonical build/test/lint/docs commands |
| `.golangci.yml` | Linter config (golangci-lint v2) — enforced in CI |
| `cmd/doc-gen/` | Regenerates `docs/` from command definitions |

## Boundaries

### ✅ Always Do
- Run `make test-unit` and `make lint` before committing.
- Add or update unit tests for new/changed behavior.
- **When you add or change a command, flag, or its help text, run `make docs`** to regenerate `docs/` — CI runs `make check-docs` and fails if `docs/` is out of sync.
- Update `README.md` in the same PR when you change installation, top-level commands, or supported integration patterns.
- Update `CHANGELOG.md` for user-facing changes.
- Return errors with `fmt.Errorf(..., %w)` wrapping; don't `os.Exit` from command logic.

### ⚠️ Ask First
- Running `make test-integration` — it hits a **live Auth0 tenant** (needs `AUTH0_DOMAIN` / `AUTH0_CLIENT_ID` / `AUTH0_CLIENT_SECRET`) and mutates real resources. The safe default is `make test-unit`.
- Adding a new dependency or bumping `go.mod`.
- Changing command names, flag names, or flag defaults (breaking change for users and scripts).
- Migrating a command between `go-auth0` v1 and v2.
- Any breaking change to command behavior or output format — confirm before proceeding.

### 🚫 Never Do
- Commit secrets, tokens, client secrets, or a real tenant domain.
- Log tokens, refresh tokens, or client secrets — they belong only in the OS keyring.
- Hand-edit anything under `docs/` (regenerate with `make docs`) or `internal/**/mock/` (regenerate with `make test-mocks`).
- Skip or delete failing tests without fixing the cause.

## Security Considerations

- **Token & secret storage:** access tokens, refresh tokens, and client secrets are stored in the OS keychain via `internal/keyring` (zalando/go-keyring) — never in plaintext config or logs.
- **Authentication:** device-authorization flow and client-credentials; handled in `internal/auth`.
- **Analytics:** `internal/analytics` sends CLI usage events; users opt out with `AUTH0_CLI_ANALYTICS=false`, and debug builds (empty version) never track. Preserve this opt-out when touching analytics.
- **Error reporting:** `internal/instrumentation` reports to Sentry only when a `SentryDSN` is configured; it is a no-op otherwise.

---

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

## Commands

```bash
make test-unit # unit tests (race + coverage) — safe, no credentials
make lint # golangci-lint (with --fix)
make build # build the auth0 binary
```

See [references/commands.md](references/commands.md) for the full list (integration tests, docs generation, mocks, vuln check, all-platform builds). Read only when you need to build, test, or generate something beyond the three above.

## Testing

Unit tests run with `make test-unit` (`go test -race -coverprofile`); the default suite is unit-only and needs no credentials. A separate live-tenant integration tier exists (`make test-integration`) — see Boundaries.

See [references/testing.md](references/testing.md) for the frameworks (testify, golang/mock), the integration harness, `FILTER` usage, and coverage. Read when writing or running tests.

## Code Style

Go, formatted with `gofmt -s` + `goimports` (local prefix `github.com/auth0/auth0-cli`). golangci-lint (`errcheck`, `revive`, `staticcheck`, `gocritic`, `godot`, …) is enforced in CI — notably **`godot`: comments must end with a period**, capitalized.

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

## Git Workflow

Branch off `main` with a short descriptive name; open a PR against `main` following the PR template. Commit messages should be clear and imperative.

See [references/git-workflow.md](references/git-workflow.md) for the full contribution flow and PR requirements. Read when preparing a PR.

## Common Pitfalls

The high-frequency traps: **forgetting `make docs` after a command/flag change** (fails CI), hand-editing generated `docs/`, and accidentally running the live-tenant integration suite.

See [references/pitfalls.md](references/pitfalls.md) for the full list with fixes (godot lint failures, go-auth0 v1/v2 confusion, keyring on headless CI, analytics opt-out). Read when a build/lint/test fails unexpectedly.

## Docs Update Rules

Tracked docs: `README.md` (hand-written) and `docs/` (generated). `docs/` is produced by `go run ./cmd/doc-gen` and verified by `make check-docs` in CI — so command/flag changes must be accompanied by regenerated docs, not hand edits.

See [references/docs-update.md](references/docs-update.md) for the code-to-docs mapping (which change updates which doc). Read when changing commands, flags, or installation.
56 changes: 56 additions & 0 deletions references/code-style.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Code Style

## Formatting & linting (CI-enforced)

- **Formatters:** `gofmt -s` (simplify) and `goimports` with local prefix `github.com/auth0/auth0-cli` (keeps repo imports in their own group). `.golangci.yml` runs both.
- **Linters** (golangci-lint v2): `errcheck`, `revive`, `staticcheck` (all checks), `gocritic`, `godot`, `unconvert`, `unused`, `whitespace`.
- **`godot`:** comments must be full sentences — **capitalized and ending with a period**. This is the most common surprise lint failure.
- **`errcheck`:** don't silently drop returned errors (the config relaxes this for some blanket cases, but prefer handling them).

## Naming

Idiomatic Go: exported `PascalCase`, unexported `camelCase`, package names short and lowercase. One file per command group under `internal/cli/` (e.g. `apps.go`, `apis.go`, `actions.go`) and a matching renderer under `internal/display/`.

## Command idiom (Cobra)

Commands are constructed as `*cobra.Command` with `Use`, `Short`, `Args`, `Example`, and `RunE` (return an error — never `os.Exit` inside command logic):

**✅ Good:**

```go
func listAppsCmd(cli *cli) *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Args: cobra.NoArgs,
Short: "List your applications",
Example: ` auth0 apps list`,
RunE: func(cmd *cobra.Command, args []string) error {
apps, err := cli.api.Client.List(cmd.Context())
if err != nil {
return fmt.Errorf("failed to list applications: %w", err)
}
cli.renderer.ApplicationList(apps)
return nil
},
}
return cmd
}
```

**❌ Bad:**

```go
func listAppsCmd(cli *cli) *cobra.Command {
return &cobra.Command{
Use: "list",
Run: func(cmd *cobra.Command, args []string) { // Run, not RunE — can't surface errors
apps, _ := cli.api.Client.List(cmd.Context()) // dropped error (errcheck)
fmt.Println(apps) // bypasses the renderer
},
}
}
```

## Errors

Wrap with context using `fmt.Errorf("...: %w", err)` so callers can unwrap; let the root command handle exit codes and Sentry reporting.
38 changes: 38 additions & 0 deletions references/commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Commands

Full command reference for auth0-cli. The canonical source is the `Makefile`; CI (`.github/workflows/main.yml`) runs `make check-docs`, `make lint`, `make test-unit`, `make test-integration`, and `make build-all-platforms`.

## Everyday

```bash
make test-unit # unit tests: go test -v -race <pkgs> -coverprofile=coverage-unit-tests.out
make lint # golangci-lint run -v --fix -c .golangci.yml ./...
make build # build ./build/auth0 for the native platform
make deps # download Go module dependencies
```

## Docs (generated)

```bash
make docs # regenerate docs/ via `go run ./cmd/doc-gen` (then moves auth0.md → index.md)
make check-docs # CI check: fails if `make docs` would change any file
```

## Tests

```bash
make test # unit + integration (integration needs a live tenant — see Boundaries)
make test-integration # full live-tenant suite
make test-integration FILTER="attack protection" # run one suite
make test-mocks # regenerate mocks via mockgen
```

## Build & release

```bash
make build-all-platforms # dev build for all supported platforms (CI verifies)
make install # install the auth0 binary for the native platform
make check-vuln # govulncheck ./...
```

Run `make help` to list every target with its description.
25 changes: 25 additions & 0 deletions references/docs-update.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Docs Update Rules

Treat docs as part of the change. For a CLI, the public surface is **commands, subcommands, and flags** — and most of the reference docs are generated from them.

## Tracked docs

| Doc | Kind | How it's maintained |
|-----|------|---------------------|
| `docs/` (per-command `.md` files, `index.md`) | **Generated** | `go run ./cmd/doc-gen` via `make docs`; verified by `make check-docs` in CI |
| `README.md` | Hand-written | Edited manually — installation, highlights, top-level command overview |
| `CHANGELOG.md` | Hand-written | Updated for every user-facing change |
| `MIGRATION_GUIDE.md`, `CUSTOMIZATION_GUIDE.md` | Hand-written | Update when the relevant behavior changes |

## When you change code, update these docs

| Code change | Update |
|-------------|--------|
| Add/rename/remove a command or subcommand | Run `make docs` (regenerates `docs/`); add it to `README.md` if it's a top-level command |
| Add/rename a flag, or change its default | Run `make docs`; mention in `README.md` if it affects a documented workflow |
| Change a command's `Short`/`Example`/help text | Run `make docs` |
| Change installation or supported platforms | `README.md` and `install.sh` |
| Any user-facing behavior change | `CHANGELOG.md` |
| Breaking change to a command/flag | `MIGRATION_GUIDE.md` + `CHANGELOG.md` (and Ask First) |

> The rule of thumb: if `make check-docs` would fail, you owe a `make docs` regeneration in the same PR. Never hand-edit files under `docs/`.
19 changes: 19 additions & 0 deletions references/git-workflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Git Workflow

Contribution flow (see `CONTRIBUTING.md` / `GENERAL-CONTRIBUTING.md`):

1. Fork/branch off `main` with a short, descriptive branch name.
2. Make the change; run `make test-unit` and `make lint` locally.
3. If you touched commands or flags, run `make docs` so `docs/` stays in sync.
4. Commit with clear, imperative messages.
5. Open a PR against `main` and fill in the PR template (loaded automatically).

## PR requirements

- CI must pass: `make check-docs`, lint (golangci-lint), `make test-unit`, `make test-integration`, and `make build-all-platforms`.
- Update `CHANGELOG.md` for user-facing changes.
- Integration tests are skipped for PRs from forks / dependabot / snyk (missing repo secrets) — maintainers verify those.

## Releases

Releases are cut with GoReleaser (`.goreleaser.yml`) via the `release.yml` / `release-beta.yml` workflows — not by hand. Don't attempt to tag or publish a release as part of a feature PR.
9 changes: 9 additions & 0 deletions references/pitfalls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Common Pitfalls

- **Forgetting to regenerate docs.** Any change to a command, subcommand, flag, or help/`Example` text changes the generated `docs/`. CI runs `make check-docs` and fails if `docs/` is stale. Fix: run `make docs` and commit the result — never hand-edit files under `docs/`.
- **`godot` lint failures.** Comments must be complete sentences: capitalized and ending with a period. This trips up otherwise-clean PRs. Run `make lint` (it applies `--fix` where it can).
- **go-auth0 v1 vs v2.** Both `github.com/auth0/go-auth0` (v1) and `.../go-auth0/v2` are in `go.mod` during an in-progress migration. Match the version the surrounding command already uses; migrating a command between them is Ask-First.
- **Accidentally running the live-tenant suite.** `make test-integration` (and bare `make test`) hit a real tenant and mutate resources. Use `make test-unit` for the normal loop.
- **Keyring on headless environments.** Token/secret storage uses the OS keychain via `internal/keyring`; on headless CI or containers the keyring backend may be unavailable — expect and handle that rather than falling back to plaintext.
- **Dropping errors.** `errcheck` and the Cobra `RunE` pattern both expect errors to be returned and wrapped (`%w`), not swallowed or sent to `os.Exit` from inside a command.
- **Analytics opt-out.** When touching `internal/analytics`, preserve the `AUTH0_CLI_ANALYTICS=false` opt-out and the debug-build guard (no tracking when `buildinfo.Version == ""`).
26 changes: 26 additions & 0 deletions references/testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Testing

## Unit tests (default, safe)

```bash
make test-unit
```

Runs `go test -v -race` across the module with a coverage profile (`coverage-unit-tests.out`). No credentials required. Test files sit next to the code they cover (`internal/cli/apps_test.go`, etc.).

- **Assertions:** standard Go testing + testify.
- **Mocks:** generated with `golang/mock` (mockgen) into `internal/**/mock/`. Regenerate with `make test-mocks` — don't hand-edit generated mocks.
- **Table-driven tests** are the dominant style; follow the existing `tests := []struct{ name string; ... }` pattern.

## Integration tests (live tenant — Ask First)

```bash
make test-integration
make test-integration FILTER="attack protection" # single suite
```

Driven by `test/integration/scripts/run-test-suites.sh` over YAML test-case files in `test/integration/*.yaml` (using the `commander` tool). These run **real `auth0` commands against a live Auth0 tenant** and require `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, and `AUTH0_CLIENT_SECRET`. They create and mutate real resources — do not run them casually; the safe default is `make test-unit`.

## Coverage

CI uploads coverage to Codecov (`codecov.yml`). Integration coverage is collected via `GOCOVERDIR` and merged with `go tool covdata`.
Loading