diff --git a/src/pages/docs/features/_meta.ts b/src/pages/docs/features/_meta.ts index a6d00f9..c2bb629 100644 --- a/src/pages/docs/features/_meta.ts +++ b/src/pages/docs/features/_meta.ts @@ -56,5 +56,8 @@ export default { }, "mcp-integration": { "title": "MCP Integration" + }, + "cli": { + "title": "CLI (lfc)" } -}; +}; \ No newline at end of file diff --git a/src/pages/docs/features/cli.mdx b/src/pages/docs/features/cli.mdx new file mode 100644 index 0000000..07213cd --- /dev/null +++ b/src/pages/docs/features/cli.mdx @@ -0,0 +1,250 @@ +--- +title: CLI (lfc) +tags: + - cli + - lfc + - operators + - tooling + - automation +--- + +import { Callout } from "nextra/components"; + +`lfc` is the command-line interface for Lifecycle. It lets you view and manage +preview environments (builds), redeploy services, stream logs, host static +sites, and validate configuration — straight from your terminal. It is built +for **humans and agents**: every command supports `--json`, human-readable +messages go to `stderr`, and machine output goes to `stdout`. + + + The CLI ships with **no deployment URLs baked in**. You point it at your own + Lifecycle deployment once with `lfc init`. All examples below use generic + placeholder hosts such as `https://lifecycle.example.com` and `myorg/myrepo` — + substitute your own values. + + +## Overview + +Requires Node ≥ 20. + +```sh +npm install -g lfc-cli +lfc --help +``` + +- **`--help` everywhere.** `lfc --help` works on every command and + subcommand and lists all flags. +- **`--json` everywhere.** Add `--json` for machine-readable output, or set + `LFC_JSON=1` to enable it globally. +- **Humans and agents.** Interactive prompts appear only on a TTY; without one, + commands fail fast and name the missing flag/argument, so scripts and agents + stay deterministic. Run `lfc llms` to print agent-oriented instructions. + +## How it works + +`lfc` is a thin client over your Lifecycle deployment's HTTP API. Nothing about +your deployment is hard-coded — you configure it once and the CLI stores a +**profile** locally. + +- **Profiles.** Each profile points at one deployment (API URL, optional UI URL, + and auth settings). You can keep several (e.g. `default`, `staging`, `local`) + and switch between them. Config lives at + `~/.config/lifecycle-cli/config.json`. +- **Authentication.** Deployments that enforce SSO use Keycloak (Authorization + Code + PKCE, or device code for headless machines). Tokens are cached per + profile under `~/.config/lifecycle-cli/tokens/` and refresh automatically. + Auth-less deployments (`authEnabled: false`) skip all of this. +- **Output & scripting.** JSON goes to `stdout`; informational messages go to + `stderr`. Commands return meaningful exit codes: + + | Code | Meaning | + | ---- | ----------------------------------------------------------- | + | `0` | success | + | `1` | failure (API error, failed build, invalid schema) | + | `2` | watch timeout (`--watch` did not converge in time) | + | `3` | not found (unknown uuid, no matching build, no config file) | + | `4` | authentication error (not logged in, expired session) | + + + On exit code `4`, run `lfc login` (or `lfc login --device` on a headless + machine) and retry. Verify who you are with `lfc whoami`. + + +## Configuration + +Point the CLI at your deployment. Interactively: + +```sh +lfc init +``` + +Or non-interactively (for scripts, dotfiles, onboarding docs): + +```sh +# SSO deployment +lfc init \ + --api-url https://lifecycle.example.com \ + --ui-url https://lifecycle-ui.example.com \ + --issuer https://sso.example.com/realms/lifecycle + +# auth-less deployment (e.g. local) +lfc init --api-url http://localhost:8080 --no-auth --name local +``` + +`init` verifies the URL is reachable, writes the profile, and (when auth is on) +starts the SSO login. Useful flags: `--ui-url` (enables `lfc builds open`), +`--no-login` (skip the login step), `--device` (device-code login), +`--name `, `--force` (overwrite an existing profile). + +### Config file structure + +Configuration lives at `~/.config/lifecycle-cli/config.json` and holds one entry +per deployment under `profiles`, with the active one named by `currentProfile`: + +```json +{ + "currentProfile": "default", + "profiles": { + "default": { + "apiUrl": "https://lifecycle.example.com", + "uiUrl": "https://lifecycle-ui.example.com", + "authEnabled": true, + "keycloak": { + "issuer": "https://sso.example.com/realms/lifecycle", + "clientId": "lifecycle-cli" + } + }, + "local": { + "apiUrl": "http://localhost:8080", + "authEnabled": false + } + } +} +``` + +| Field | Description | +| ------------------- | ------------------------------------------------------------------- | +| `apiUrl` | Base URL of the Lifecycle API (required). | +| `uiUrl` | Base URL of the Lifecycle UI (optional; enables `lfc builds open`). | +| `authEnabled` | Whether the deployment enforces SSO. | +| `keycloak.issuer` | Keycloak realm issuer URL (when `authEnabled` is `true`). | +| `keycloak.clientId` | OAuth client id (defaults to `lifecycle-cli`). | + +Manage profiles without editing the file by hand: + +```sh +lfc config list # all profiles +lfc config get # active profile as JSON (no secrets) +lfc config add-profile staging --api-url https://lifecycle-staging.example.com --issuer +lfc config use-profile staging # switch the active profile +lfc --profile staging builds list # one-off profile override +``` + +### Environment variables + +| Variable | Effect | +| ------------------- | --------------------------------------- | +| `LIFECYCLE_API_URL` | Override the API base URL. | +| `LFC_PROFILE` | Select the active profile by name. | +| `LFC_JSON=1` | Force JSON output globally. | +| `LFC_CONFIG_DIR` | Override the config directory location. | + +One-off usage with no saved config (auth-less deployments only): + +```sh +lfc --api-url http://localhost:8080 builds list +``` + +## Commands + +### Authentication + +```sh +lfc login # SSO in the browser +lfc login --device # headless: prints a URL + code to confirm elsewhere +lfc whoami # logged-in user, roles, token expiry +lfc logout +``` + +### Builds (preview environments) + +```sh +lfc builds list [--mine] [--search ] [--all] [-n ] [-p ] +lfc builds get [--manifest] +lfc builds find --pr [--repo myorg/myrepo] # resolve a PR/branch → build +lfc builds find --branch --repo myorg/myrepo +lfc builds status [--watch] # exit 0 deployed, 1 failed, 2 timeout +lfc builds redeploy [--watch] +lfc builds destroy --yes +lfc builds update-uuid +lfc builds set --static | --no-static | --track-defaults | --no-track-defaults +lfc builds webhooks [--invoke] +lfc builds open [--print] # open in the Lifecycle UI +``` + +`builds find` resolves a build from a PR or branch — useful when you have a PR +URL but not the uuid. A PR URL is self-contained; a bare PR number or a branch +needs `--repo`. With `--json` it returns `{ found, build, matches }` (more than +one `matches` entry means the selector was ambiguous). Exit `0` found, `3` no +build yet (e.g. the PR was just opened), `1` if the scan was truncated before +resolving. + +Per-build environment-variable overrides: + +```sh +lfc builds env get +lfc builds env set KEY=VALUE [KEY=VALUE ...] [--init] +lfc builds env unset KEY [KEY ...] +``` + +### Services (within a build) + +```sh +lfc services list [--all] +lfc services redeploy +lfc services enable | disable +lfc services set-branch +lfc services history # build/deploy job history +lfc services logs [--deploy] [--job ] [-f] +``` + +### Pods & runtime logs + +```sh +lfc pods list [--service ] +lfc pods logs [-c ] [-f] [--tail ] [--timestamps] +``` + +### Schema validation + +```sh +lfc schema validate [path] # finds lifecycle.yaml locally (offline) +lfc schema validate --repo myorg/myrepo --branch # server-side check +``` + +Exit `0` valid, `1` invalid (per-field errors printed), `3` no config file found. + +### Static sites + +```sh +lfc sites create [--name