96 lines
3.6 KiB
Markdown
96 lines
3.6 KiB
Markdown
# knecht
|
|
|
|
CLI + TUI for managing Portainer stacks from the local `services/` directory.
|
|
|
|
## Installation
|
|
|
|
```sh
|
|
cd knecht
|
|
go build -o knecht .
|
|
# move to somewhere on your PATH, e.g.:
|
|
mv knecht /usr/local/bin/knecht
|
|
```
|
|
|
|
## Configuration
|
|
|
|
`~/.config/knecht/config.toml`:
|
|
|
|
```toml
|
|
url = "https://portainer.example.com"
|
|
token = "ptr_..."
|
|
ignore = ["portainer"] # stack names to hide (e.g. self-managed containers)
|
|
|
|
# Optional
|
|
endpoint = "local" # Portainer environment name, auto-detected if omitted
|
|
services_path = "/path/to/services" # defaults to git root / services
|
|
```
|
|
|
|
The API token is generated in Portainer under **Account → Access tokens**.
|
|
|
|
## Commands
|
|
|
|
| Command | Description |
|
|
|---------|-------------|
|
|
| `knecht` | Launch the TUI |
|
|
| `knecht list` | Print all stacks with status and drift summary |
|
|
| `knecht deploy <stack>` | Deploy a new stack from `services/<stack>/docker-compose.yml` |
|
|
| `knecht update <stack>` | Update an existing stack, preserving Portainer env vars |
|
|
| `knecht restart <stack>` | Stop then start a stack |
|
|
| `knecht diff <stack>` | Print compose and env key drift |
|
|
| `knecht logs [container]` | Open Dozzle in the browser; deep-links to a specific container if given |
|
|
|
|
`<stack>` always maps to a folder name under `services/`.
|
|
|
|
## TUI
|
|
|
|
Run `knecht` with no arguments to launch the interactive TUI.
|
|
|
|
```
|
|
┌─ stacks ──────────────┐ ┌─ detail ──────────────────────────────┐
|
|
│ ● traefik live │ │ rrr │
|
|
│ ● rrr live ~ │ │ │
|
|
│ ● jellyfin live │ │ status: running │
|
|
│ dummy not deploy │ │ drift: compose ~3 lines │
|
|
│ │ │ │
|
|
│ │ │ [u] update [d] diff [r] refresh │
|
|
└───────────────────────┘ └──────────────────────────────────────┘
|
|
```
|
|
|
|
### Keybinds
|
|
|
|
| Key | Action |
|
|
|-----|--------|
|
|
| `↑` / `↓` | Navigate stacks |
|
|
| `u` | Update selected stack (preserves Portainer env vars) |
|
|
| `D` | Deploy selected stack (only available when not deployed) |
|
|
| `d` | Toggle diff view |
|
|
| `e` | Generate or append `.env.example` from Portainer env keys |
|
|
| `r` | Refresh all stacks |
|
|
| `esc` | Return to summary view |
|
|
| `q` / `ctrl+c` | Quit |
|
|
|
|
## Drift Detection
|
|
|
|
knecht compares the local `services/<stack>/docker-compose.yml` against what is deployed in Portainer, and the keys in `services/<stack>/.env.example` against the env vars set in Portainer.
|
|
|
|
| Indicator | Meaning |
|
|
|-----------|---------|
|
|
| `~` next to stack name | Drift detected |
|
|
| `! missing: KEY` | Key is in `.env.example` but not set in Portainer |
|
|
| `? unknown: KEY` | Key is in Portainer but not in `.env.example` — press `e` to append |
|
|
| `no .env.example` | Portainer has env vars but no example file exists — press `e` to generate |
|
|
|
|
Stacks with no `.env.example` file are not penalised for env drift — absence of the file means "no opinion".
|
|
|
|
## Stack Convention
|
|
|
|
Each stack is a directory under `services/` containing:
|
|
|
|
```
|
|
services/<name>/
|
|
├── docker-compose.yml # required
|
|
└── .env.example # optional — documents required Portainer env vars
|
|
```
|
|
|
|
`knecht deploy <name>` / `knecht update <name>` read these files directly. Env var *values* are never stored locally — only the keys in `.env.example`, as documentation.
|