noyafmt and noyavalidate —
the YAML formatter and validator that ship as the CLI half of
the noyalib workspace.
- Install — every channel mapped
- Quick Start — common workflows
noyafmt— formatter referencenoyavalidate— validator + autofix reference- Exit codes — for shell pipelines and CI gates
- Examples — runnable demo scripts
- Shell completions and man pages
- Verification — cosign + SLSA cookbook
- When not to use these tools
- Documentation
- License
| Channel | Command |
|---|---|
| Cargo (crates.io) | cargo install noya-cli --locked |
| Cargo (from source) | cargo install --locked --path . |
| Homebrew (personal tap) | brew tap sebastienrousseau/tap && brew install noyalib |
| Arch (AUR) | yay -S noyalib-bin (binary) or yay -S noyalib (source) |
| Scoop (Windows) | scoop bucket add sebastienrousseau https://github.com/sebastienrousseau/scoop-bucket && scoop install noyalib |
| Nix | nix run github:sebastienrousseau/noyalib |
| Container (GHCR) | docker run --rm -v "$(pwd):/work" -w /work ghcr.io/sebastienrousseau/noyafmt:latest --check ci/*.yaml |
Pre-built tarballs for 14 targets (Linux gnu + musl, macOS
Intel + Apple Silicon + universal, Windows x86_64 + i686 +
aarch64) are attached to every GitHub Release. Each artefact is
signed with cosign keyless and carries a SLSA L3 build provenance
attestation — see
Verification for the verify commands or
pkg/VERIFY.md
for the full cookbook.
MSRV: Rust 1.85.0. The clap dep tree pulls
clap_builder 4.6 (edition 2024); the noyalib core library
itself stays at 1.75.
# Format a file in-place; comments + indentation preserved.
noyafmt --write config.yaml
# CI gate — exits 1 if any file would change.
noyafmt --check ci/*.yaml
# Validate syntax + JSON Schema 2020-12.
noyavalidate --schema schema.yaml deploy.yaml
# Validate + auto-fix obvious type slips (port: "8080" → 8080).
noyavalidate --schema schema.yaml --fix deploy.yamlYAML formatter mirroring the rustfmt / prettier ergonomics:
noyafmt config.yaml # print formatted source to stdout (default)
noyafmt --write config.yaml # rewrite in place
noyafmt --check ci/*.yaml # CI gate
noyafmt --indent 4 config.yaml # override default 2-space indent
cat foo.yaml | noyafmt --stdin # editor pipe (Vim, Emacs, …)
git ls-files '*.yaml' | xargs noyafmt --checkThe formatter runs through noyalib's lossless CST: comments, anchor positions, and document structure are preserved byte-for-byte; only whitespace and quoting are normalised.
| Flag | Effect |
|---|---|
--check |
Verify each FILE is formatted; print files that need formatting; exit 1 if any do. Non-destructive. |
--write |
Rewrite each FILE in place. Default is to print to stdout. Mutually exclusive with --check. |
--stdin |
Read from stdin, write to stdout. Mutually exclusive with FILE arguments. |
--indent N |
Indentation width in spaces (default: 2). |
YAML syntax checker with optional JSON Schema 2020-12 enforcement and schema-driven autofix.
noyavalidate manifest.yaml # syntax only
noyavalidate --schema schema.yaml deploy.yaml # + schema check
noyavalidate --schema schema.yaml --fix deploy.yaml # + autofix
cat manifest.yaml | noyavalidate # stdinThe autofix engine
(coerce_to_schema)
rewrites string-shaped scalars into the schema's expected type
when the parse succeeds. Loops until convergence; unparseable
inputs (port: "abc" against type: integer) are left in place
so a follow-up validate_against_schema call surfaces the
residue.
Diagnostics use miette for
rustc-style source pointers:
× schema violation: "8080" is not of type "integer"
╭─[deploy.yaml:3:7]
2 │ replicas: 3
3 │ port: "8080"
· ─┬───
· ╰── here
4 │ host: api
╰────
help: pass --fix to coerce string-shaped scalars to the
schema's declared type.
| Flag | Effect |
|---|---|
-s, --schema PATH |
Validate each document against JSON Schema 2020-12 at PATH (the schema may itself be YAML or JSON). |
--fix |
Rewrite FILE in place via the CST formatter (lossless: byte-faithful for everything except normalised whitespace / line endings). With stdin input, the formatted bytes go to stdout. |
-q, --quiet |
Suppress success output. |
| Code | noyafmt |
noyavalidate |
|---|---|---|
| 0 | success (or no changes if --check) |
all valid |
| 1 | parse / I/O error, or --check found unformatted file(s) |
parse error or schema violation |
| 2 | invalid usage (bad arg combination) | invalid usage |
| 3 | — | I/O error (read / write) |
End-to-end runnable demos under
crates/noya-cli/examples/:
| Script | What it shows |
|---|---|
format-precommit.sh |
Drop-in git pre-commit hook gating commits on noyafmt --check. |
validate-k8s.sh |
CI step that runs noyavalidate --schema over a directory of Kubernetes manifests. |
fix-quoted-numbers.sh |
Walkthrough of the --fix autofix flow: quoted scalar → schema-typed integer, with the surrounding comment preserved. |
chmod +x crates/noya-cli/examples/*.sh
crates/noya-cli/examples/fix-quoted-numbers.shTarball releases ship pre-built completions for bash, fish, zsh,
and PowerShell, plus roff man pages. Distro packages drop them
into the standard system locations
(/usr/share/bash-completion/completions/,
/usr/share/man/man1/, …).
If installing via cargo install, regenerate locally:
git clone https://github.com/sebastienrousseau/noyalib
cd noyalib
cargo xtask completions # writes complete/{noyafmt,noyavalidate}.{bash,fish,zsh,ps1}
cargo xtask manpages # writes doc/{noyafmt,noyavalidate}.1Every release artefact ships with a cosign keyless signature
(<artefact>.sig + <artefact>.pem) and a SLSA L3 build
provenance attestation. Verify before trusting a downloaded
binary:
COSIGN_EXPERIMENTAL=1 cosign verify-blob \
--certificate-identity-regexp 'https://github.com/sebastienrousseau/noyalib/' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
--certificate <artefact>.pem \
--signature <artefact>.sig \
<artefact>
gh attestation verify --owner sebastienrousseau <artefact>Full cookbook including the offline / FIPS-bound flow:
pkg/VERIFY.md.
- You need to format YAML faster than human-perceivable
latency.
noyafmtruns end-to-end on a 1 MiB document in ~10 ms; for<100 KBdocuments that's already invisible. But for a streaming editor pipeline that wants per-keystroke formatting, the LSP server (noyalib-lsp) issues incrementalTextEdit[]s instead. - You need YAML 1.1-only behaviour, top to bottom.
noyavalidatefollows YAML 1.2; thelegacy_booleansopt-in is exposed at the library level but not yet plumbed through the CLI. - You need to embed the formatter or validator in your own
Rust binary. Use the
noyaliblibrary directly — every CLI feature flows through public library APIs (cst::format_with_config,validate_against_schema,coerce_to_schema).
- Engineering policies (MSRV, SemVer, security, performance, concurrency, platform support, feature flags):
doc/POLICIES.md - Security policy:
SECURITY.md - CLI flag reference:
doc/cli-reference.md - Recipes (CI gates, pre-commit, editor integration):
doc/recipes.md - Workspace README: https://github.com/sebastienrousseau/noyalib#readme
- Per-channel install + verify:
pkg/VERIFY.md - Library API the binaries call into: https://docs.rs/noyalib
Dual-licensed under Apache 2.0 or MIT, at your option.