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
1 change: 1 addition & 0 deletions .vale/styles/spelling-exceptions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,4 @@ yamllint
YouTube
vscode
VSCode
walkthrough
21 changes: 21 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

Infrahub Python SDK - async/sync client for Infrahub infrastructure management.

## Product context

The SDK is the foundational library for programmatically interacting with Infrahub. It abstracts away the underlying API so developers can work with infrastructure data using native Python objects.

**Primary audience:** Network automation engineers and software developers.

**Three main use cases:**

- **Automate inside Infrahub** — Write transforms, generators, and checks that run as part of Infrahub's pipeline.
- **Integrate with external systems** — Query and sync data between Infrahub and existing tools. `infrahubctl` and the Infrahub Ansible collection both use this SDK internally.
- **Build custom applications** — Use Infrahub as a data backend for Python projects entirely outside of Infrahub's own pipeline.

**Why the SDK over direct API calls:** eliminates the need to learn Infrahub's API structure, provides Python-native interfaces with built-in auth, adds advanced capabilities (batching, caching, tracking), and reduces boilerplate.

## Commands

```bash
Expand Down Expand Up @@ -73,6 +87,13 @@ Key rules:
- Modify generated code (protocols.py)
- Bypass type checking without justification

## Knowledge base

Deep-dive docs on architecture and workflows live in `dev/knowledge/`. Read these before making changes to the areas they cover.

- [dev/knowledge/cli-architecture.md](dev/knowledge/cli-architecture.md) - CLI command hierarchy and design rules
- [dev/knowledge/doc-generation.md](dev/knowledge/doc-generation.md) - How docs are auto-generated from code

## Subdirectory guides

- [docs/AGENTS.md](docs/AGENTS.md) - Documentation (Docusaurus)
Expand Down
51 changes: 51 additions & 0 deletions dev/knowledge/cli-architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# CLI Architecture

The `infrahubctl` CLI is built with [Typer](https://typer.tiangolo.com/) via a custom `AsyncTyper` subclass that supports async command functions.

## Entry point

The main Typer app lives in `infrahub_sdk/ctl/cli_commands.py`. It is re-exported through `infrahub_sdk/ctl/cli.py` which adds the `infrahubctl` entry point name.

## Command hierarchy

Commands are organized in two tiers:

- **Root commands** are registered directly on the main app with `app.command()`. These are standalone operations that don't belong to a logical group (e.g. `dump`, `load`, `check`, `render`, `run`, `transform`, `protocols`, `version`, `info`).
- **Subcommand groups** are separate `AsyncTyper()` instances registered with `app.add_typer(sub_app, name="group")`. Each group lives in its own module under `infrahub_sdk/ctl/`. Current groups: `branch`, `schema`, `validate`, `repository`, `menu`, `object`, `graphql`, `task`.

## Adding a new command

For a **root command**, define the function in the appropriate module and register it in `cli_commands.py`:

```python
app.command(name="mycommand")(my_function)
```

For a **subcommand**, add it to the relevant group's package or module. For example, object subcommands live in `infrahub_sdk/ctl/object/` and are registered on the object app in `__init__.py`.

## Group packages

When a subcommand group has multiple commands, it lives as a package (directory with `__init__.py`) rather than a single module file. The `object` group is the reference example:

```text
infrahub_sdk/ctl/object/
├── __init__.py # App, callback, load/validate commands, registers CRUD
├── create.py # create subcommand
├── delete.py # delete subcommand
├── get.py # get subcommand
├── update.py # update subcommand
└── utils.py # Shared utilities (resolve_node, etc.)
```

Each command file contains a single command function. Shared logic goes in `utils.py`. The `__init__.py` wires everything together by importing and registering commands on the group's `AsyncTyper` app. Other groups that grow beyond a single file should follow this same pattern.

## Decorators

- `@catch_exception(console=console)` wraps commands for consistent error handling via Rich.
- Async commands work natively thanks to `AsyncTyper`.

## Design rules

**Always check if a new command belongs in an existing group before adding it at the root.** A command that operates on a specific resource type (objects, branches, schemas, etc.) should go under the matching subgroup, not at the top level. Root-level commands are reserved for cross-cutting or standalone operations (e.g. `run`, `version`, `info`).

When in doubt, look at what the command acts on and find the group that matches. For example, anything that creates, reads, updates, or deletes Infrahub objects belongs under `object`, not at the root.
40 changes: 40 additions & 0 deletions dev/knowledge/doc-generation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Documentation Generation

CLI and SDK documentation is auto-generated from code. Always regenerate after changing commands, config, or public docstrings.

## How to run

```bash
uv run invoke docs-generate # Generate all docs (CLI + SDK)
uv run invoke docs-validate # Verify generated docs match committed versions
```

## CLI documentation

Defined in `tasks.py` (`_generate_infrahubctl_documentation`). The process:

1. Deletes all existing `infrahubctl-*.mdx` files in `docs/docs/infrahubctl/`.
2. Iterates `app.registered_commands` and creates a `TyperSingleCommand` for each.
3. Iterates `app.registered_groups` and creates a `TyperGroupCommand` for each.
4. Each command object generates an mdx file via `typer ... utils docs`.

### How it maps to files

- A **root command** named `foo` produces `infrahubctl-foo.mdx` using:
`uv run typer --func foo infrahub_sdk.ctl.cli_commands utils docs --name "infrahubctl foo"`
- A **subcommand group** named `bar` produces `infrahubctl-bar.mdx` using:
`uv run typer infrahub_sdk.ctl.bar utils docs --name "infrahubctl bar"`

The group variant documents all subcommands within that group automatically.

### Key implication

Moving a command from root to a group (or vice versa) changes which mdx files get generated. The old files are cleaned up automatically by the glob delete, but the new ones only appear after running `docs-generate`. Always regenerate and commit the result.

## SDK documentation

Other doc generators cover SDK config, compatibility matrix, templates, and API reference. These are independent of CLI structure and are also triggered by `docs-generate`.

## Validation in CI

`docs-validate` diffs the generated output against the committed files. If they don't match, CI fails. This ensures docs stay in sync with code.
31 changes: 0 additions & 31 deletions docs/docs/infrahubctl/infrahubctl-create.mdx

This file was deleted.

30 changes: 0 additions & 30 deletions docs/docs/infrahubctl/infrahubctl-delete.mdx

This file was deleted.

41 changes: 0 additions & 41 deletions docs/docs/infrahubctl/infrahubctl-get.mdx

This file was deleted.

134 changes: 134 additions & 0 deletions docs/docs/infrahubctl/infrahubctl-object.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,112 @@ $ infrahubctl object [OPTIONS] COMMAND [ARGS]...

**Commands**:

* `create`: Create a new object in Infrahub.
* `delete`: Delete an Infrahub object.
* `get`: Query and display Infrahub objects.
* `load`: Load one or multiple objects files into...
* `update`: Update an existing object in Infrahub.
* `validate`: Validate one or multiple objects files.

## `infrahubctl object create`

Create a new object in Infrahub.

Provide field values with repeatable --set flags or supply a
JSON/YAML object file via --file. The two modes are mutually exclusive.

Examples:
infrahubctl object create InfraDevice --set name=spine01 --set status=active
infrahubctl object create InfraDevice --set name=spine01 --set location=DC1
infrahubctl object create InfraDevice --file devices.yml

**Usage**:

```console
$ infrahubctl object create [OPTIONS] KIND
```

**Arguments**:

* `KIND`: Infrahub schema kind to create [required]

**Options**:

* `--set TEXT`: Field value in key=value format
* `-f, --file PATH`: JSON or YAML file with object data
* `-b, --branch TEXT`: Target branch
* `--config-file TEXT`: [env var: INFRAHUBCTL_CONFIG; default: infrahubctl.toml]
* `--help`: Show this message and exit.

## `infrahubctl object delete`

Delete an Infrahub object.

Fetches the object by KIND and IDENTIFIER, then deletes it.
Unless --yes is provided, a confirmation prompt is shown first.

Examples:
infrahubctl object delete InfraDevice spine01
infrahubctl object delete InfraDevice spine01 --yes

**Usage**:

```console
$ infrahubctl object delete [OPTIONS] KIND IDENTIFIER
```

**Arguments**:

* `KIND`: Infrahub schema kind [required]
* `IDENTIFIER`: UUID, name, or HFID (use / for multi-part, for example: Cisco/NX-OS) [required]

**Options**:

* `-y, --yes`: Skip confirmation prompt
* `-b, --branch TEXT`: Target branch
* `--config-file TEXT`: [env var: INFRAHUBCTL_CONFIG; default: infrahubctl.toml]
* `--help`: Show this message and exit.

## `infrahubctl object get`

Query and display Infrahub objects.

When IDENTIFIER is omitted the command lists all objects of the given
KIND. When IDENTIFIER is provided it displays a single object in
detail view. Empty columns are hidden by default (use --all-columns).

Examples:
infrahubctl object get InfraDevice
infrahubctl object get InfraDevice spine01
infrahubctl object get InfraDevice --filter name__value=spine01
infrahubctl object get InfraDevice --output json
infrahubctl object get InfraDevice --output yaml > backup.yml

Exit codes: 0 = results found, 1 = error (including not found in detail
mode), 80 = list query succeeded but returned zero objects.

**Usage**:

```console
$ infrahubctl object get [OPTIONS] KIND [IDENTIFIER]
```

**Arguments**:

* `KIND`: Infrahub schema kind to query [required]
* `[IDENTIFIER]`: UUID, name, or HFID (use / for multi-part, for example: Cisco/NX-OS)

**Options**:

* `--filter TEXT`: Filter in attr__value=x format
* `-o, --output [table|json|csv|yaml]`: Output format
* `-b, --branch TEXT`: Target branch
* `--limit INTEGER`: Maximum results
* `--offset INTEGER`: Skip first N results
* `--all-columns`: Show all columns including empty ones
* `--config-file TEXT`: [env var: INFRAHUBCTL_CONFIG; default: infrahubctl.toml]
* `--help`: Show this message and exit.

## `infrahubctl object load`

Load one or multiple objects files into Infrahub.
Expand All @@ -40,6 +143,37 @@ $ infrahubctl object load [OPTIONS] PATHS...
* `--config-file TEXT`: [env var: INFRAHUBCTL_CONFIG; default: infrahubctl.toml]
* `--help`: Show this message and exit.

## `infrahubctl object update`

Update an existing object in Infrahub.

Fetches the object by KIND and IDENTIFIER, applies the requested
changes, and saves back to the server. Use --set or --file.

Examples:
infrahubctl object update InfraDevice spine01 --set status=active
infrahubctl object update InfraDevice spine01 --set location=DC1
infrahubctl object update InfraDevice spine01 --file updates.yml

**Usage**:

```console
$ infrahubctl object update [OPTIONS] KIND IDENTIFIER
```

**Arguments**:

* `KIND`: Infrahub schema kind [required]
* `IDENTIFIER`: UUID, name, or HFID (use / for multi-part, for example: Cisco/NX-OS) [required]

**Options**:

* `--set TEXT`: Field value in key=value format
* `-f, --file PATH`: JSON or YAML file with update data
* `-b, --branch TEXT`: Target branch
* `--config-file TEXT`: [env var: INFRAHUBCTL_CONFIG; default: infrahubctl.toml]
* `--help`: Show this message and exit.

## `infrahubctl object validate`

Validate one or multiple objects files.
Expand Down
Loading