kubectl-craft

module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 2, 2026 License: Apache-2.0

README

kubectl-craft



CI Go version License

A kubectl plugin that presents a TUI for composing Kubernetes Manifests from the Type Schemas defined on the cluster your current context points to. Navigate a Kind's field tree with per-field documentation alongside, fill values with type-appropriate widgets, Validate the Draft against the live API server (server-side dry-run — nothing is ever persisted), and Emit the finished Manifest to stdout. Browsing the schemas is the substrate; composing is the product. The tool never mutates the cluster.

Demo

The demo is scripted in demo.tape and reproducible in one command (mise run demo:render, a vhs recording against a throwaway k3d cluster) — see CONTRIBUTING.md — Demo for the recipe. The rendered GIF is published as a release asset rather than committed (it outweighs the repo's large-file cap); the release workflow re-renders it from the tape on every tag, so the recording above is exactly as fresh as the release it shipped with.

The signature loop it records: pick a Kind, /-search a Field Path, fill values, v Validate (a CEL violation marks the offending tree node), n jump to the finding, fix it, revalidate to a clean pass, ctrl+d Emit.

Install

GitHub Releases (recommended): download the archive for your platform from the releases page, extract it, and put the kubectl-craft binary anywhere on your PATH. That is all a kubectl plugin installation needs — kubectl discovers plugins by the kubectl-<name> binary naming convention, so kubectl craft works as soon as kubectl-craft is on PATH. (kubectl krew install craft is coming as a fast-follow once v0.1 is stable.)

From source:

go install github.com/thezmc/kubectl-craft/cmd/kubectl-craft@latest

Quickstart

kubectl craft > deployment.yaml && kubectl apply -f deployment.yaml

The TUI renders to your terminal while stdout carries nothing but the Emitted Manifest, so redirecting just works. With no argument the Session opens on the Kind picker — a fuzzy-filterable list of every create-capable Kind on the cluster. An optional positional argument in kubectl-explain syntax deep-links straight to a Kind, or a Field Path within it:

kubectl craft deploy                # apps/v1 Deployment, short name resolved via discovery
kubectl craft deploy.spec.strategy  # jump to a Field Path inside its Type Schema

Standard kubectl plugin flags (--context, --kubeconfig, --namespace, …) apply; the context is fixed for the Session.

Keybindings

Keys are fixed (no rebinding config in v0.1) so this table, the in-app ? help, and the hint bar stay trivially truthful. This table mirrors the ? help overlay in the compose view.

Compose view — navigate mode
Key Action
j/k, / move focus
l, expand the focused field; on an expanded field: step to its first child
h, collapse the focused field; on a collapsed field: jump to its parent
enter open the value widget on a leaf; toggle expansion on a parent
a append an item on an array node; prompt for a key on a map node
d unset the focused value — a subtree with filled values confirms the discard count first
e pop a schema-blind subtree out to $EDITOR as raw YAML
g / G jump to the top / bottom of the tree
/ search the Kind's Field Paths and jump to a match
v Validate the Draft against the live cluster (server dry-run) — nothing persists; findings mark the offending tree nodes, and anything unmappable lands in the results pane. Editing the Draft marks findings stale (v revalidates); switching the version drops them entirely
n jump to the first Validate finding's node; pressing again cycles through the findings in order
r open the Validate results pane
V switch the open Kind's version — values carry over by Field Path, and a drop report confirms anything that would not survive
? open the help overlay (any key closes it)
Edit mode — enter on a leaf opens its type-appropriate widget

A schema-blind leaf opens the raw-YAML text area instead.

Key Action
enter confirm the value into the Draft; rejections render inline
esc cancel back to navigate mode without touching the Draft
space, / flip a boolean toggle
/ choose from an enum select
ctrl+s confirm the raw-YAML text area — enter types its newlines
Session
Key Action
esc return to the Kind picker — a non-empty Draft warns before discarding
q quit — a non-empty Draft offers Emit & quit / Discard & quit / Cancel
ctrl+d emit the Manifest to stdout and quit — no menu
ctrl+c quit immediately, discarding the Draft
Search surfaces (Kind picker and the / field-search overlay)

Type-to-filter, fzf-style: printable keys filter immediately, / (or ctrl+j/ctrl+k) move the selection, enter selects, esc clears-then-dismisses.

Validate

v submits the Draft to your API server as a server-side dry-run (dryRun=All): full schema validation, CRD CEL rules, and admission webhooks run for real, and nothing is persisted. Findings with field paths annotate the offending tree nodes (n jumps to them); anything unmappable — freeform webhook denials, top-level failures — lands in the results pane (r).

Validate needs metadata.name (plus a namespace for namespaced Kinds — taken from metadata.namespace if set, else --namespace/the kubeconfig context default, exactly like kubectl). It also needs RBAC permission to create the Kind (a dry-run create is still a create to the authorizer). An RBAC 403 or a network failure renders as "Validate unavailable" in the results pane — clearly distinct from Manifest errors.

Contracts

  • Clean stdout: the TUI renders to the terminal (/dev/tty); stdout carries nothing but the final Emitted Manifest. Redirection is the workflow, not a special mode.
  • Sparse emission: the Emitted YAML contains exactly the fields you set (plus the apiVersion/kind identity). Schema defaults appear as dimmed placeholders in the tree and in the detail pane, never in the output.
  • Nothing persists: Drafts are ephemeral — they live and die with the Session, and Validate never creates anything on the cluster.
  • The cache is disposable: OpenAPI v3 group documents are cached on disk at os.UserCacheDir()/kubectl-craft (~/.cache/kubectl-craft under XDG semantics; macOS/Windows equivalents handled by Go), keyed by cluster and server content hash and validated against the live index every Session. Deleting the directory at any time is always safe — worst case is a slower next Session. See docs/DESIGN.md — Data layer.

Supported platforms

Linux and macOS (amd64 and arm64) are supported. Any cluster serving /openapi/v3 (roughly Kubernetes 1.24+) works; the tool fails with a clear minimum-version error otherwise.

Windows artifacts are not shipped in v0.1: the TUI acquires its controlling terminal via /dev/tty, which does not exist on Windows, so a Windows binary could never run — it would exit gracefully with an actionable error, but always. Rather than publish archives that cannot work, releases skip Windows until console acquisition lands with a real test story (issue #15 records the decision). On any platform, running without a controlling terminal (for example under a plain pipe with no TTY) exits non-zero with a clear error and never panics.

Development

Start with CONTRIBUTING.md (toolchain, test loops, fixture corpora, demo regeneration). The domain language lives in CONTEXT.md, the decided product shape in docs/DESIGN.md, irreversible decisions in docs/adr/, and the build slicing in docs/MILESTONES.md.

Directories

Path Synopsis
cmd
kubectl-craft command
Package main is the entrypoint for the kubectl-craft plugin binary (named per ADR-0005).
Package main is the entrypoint for the kubectl-craft plugin binary (named per ADR-0005).
hack
capture-fixtures command
Command capture-fixtures (re)generates the checked-in fixture corpus of raw OpenAPI v3 group documents that backs the hermetic schema specs (DESIGN.md — Testing).
Command capture-fixtures (re)generates the checked-in fixture corpus of raw OpenAPI v3 group documents that backs the hermetic schema specs (DESIGN.md — Testing).
capture-status-fixtures command
Command capture-status-fixtures (re)generates the checked-in corpus of recorded dry-run Status payloads that backs the hermetic Validate specs (internal/validate/testdata), following the raw OpenAPI corpus precedent (hack/capture-fixtures).
Command capture-status-fixtures (re)generates the checked-in corpus of recorded dry-run Status payloads that backs the hermetic Validate specs (internal/validate/testdata), following the raw OpenAPI corpus precedent (hack/capture-fixtures).
generate-giant-fixture command
Command generate-giant-fixture (re)generates the giant fixture documents in internal/schema/testdata from the deterministic generator in test/giantcrd — the huge-CRD perf pass's corpus entry (MILESTONES.md — M5).
Command generate-giant-fixture (re)generates the giant fixture documents in internal/schema/testdata from the deterministic generator in test/giantcrd — the huge-CRD perf pass's corpus entry (MILESTONES.md — M5).
internal
data
Package data is the cluster-facing layer.
Package data is the cluster-facing layer.
matchers
Package matchers keeps specs in domain language: a small internal gomega matcher package speaking the CONTEXT.md glossary (DESIGN.md — Testing).
Package matchers keeps specs in domain language: a small internal gomega matcher package speaking the CONTEXT.md glossary (DESIGN.md — Testing).
schema
Package schema is the pure compose core.
Package schema is the pure compose core.
tui
Package tui is the presentation layer.
Package tui is the presentation layer.
validate
Package validate is the pure Validate core.
Package validate is the pure Validate core.
test
cluster
Package cluster pins the conformant cluster that every cluster-facing tool boots: the integration suite (test/integration) and the fixture-capture tool (hack/capture-fixtures) both import this pin, so the captured corpus and the live specs always describe one cluster version — the image cannot be bumped in one place without the other.
Package cluster pins the conformant cluster that every cluster-facing tool boots: the integration suite (test/integration) and the fixture-capture tool (hack/capture-fixtures) both import this pin, so the captured corpus and the live specs always describe one cluster version — the image cannot be bumped in one place without the other.
giantcrd
Package giantcrd deterministically generates the giant fixture: a pathological OpenAPI v3 group document on the order of real-world giant CRDs' shapes but an order of magnitude past their size, backing the huge-CRD perf pass (MILESTONES.md — M5).
Package giantcrd deterministically generates the giant fixture: a pathological OpenAPI v3 group document on the order of real-world giant CRDs' shapes but an order of magnitude past their size, backing the huge-CRD perf pass (MILESTONES.md — M5).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL