Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Noyalib logo

noya-cli

noyafmt and noyavalidate — the YAML formatter and validator built on the noyalib library.

Build Crates.io Docs.rs GitHub Release OpenSSF Scorecard OpenSSF Best Practices


Contents


Install

ChannelCommand
Pre-built binariesdownload for 8 targets (Linux gnu/musl, macOS, Windows on x86_64/aarch64) from the latest release — each with manpages, completions, SHA-256, and SLSA provenance
Linux packages.deb and .rpm on the latest release
Homebrew (macOS/Linux)brew install sebastienrousseau/tap/noya-cli
Scoop (Windows)scoop bucket add sebastienrousseau https://github.com/sebastienrousseau/scoop-bucket then scoop install noya-cli
AUR (Arch)noyalib-bin, e.g. yay -S noyalib-bin
Cargo (crates.io)cargo install noya-cli --locked
Cargo (from source)cargo install --locked --path .
GNU Make (binaries + manpages + completions)make install — honors PREFIX (default /usr/local) and DESTDIR; make uninstall reverses it
Container (GHCR)docker run --rm -v "$(pwd):/work" -w /work ghcr.io/sebastienrousseau/noya-cli:latest config.yaml validates; add --entrypoint noyafmt to format. Multi-arch, SLSA-attested, cosign-signed; first published with v0.0.41.

Formatter only, if you do not need noyavalidate

cargo install noya-cli --locked --no-default-features --features noyafmt

The default install includes noyavalidate, whose schema errors are rendered by miette with source excerpts and carets — worth the weight for a validator, since pointing at the offending line is the job. It does mean the default pulls 130 crates against 32 for the formatter alone, most of the difference being miette’s fancy renderer and its backtrace stack.

If you only ever run noyafmt, the command above is a smaller supply chain and a faster build. Nothing is degraded for anyone who wants the diagnostics — it is a choice, not a default.

Releases ship the crate archive with a CycloneDX SBOM and sigstore bundles, plus the pre-built binary archives and Linux packages above, each with SHA-256 and a SLSA build-provenance attestation — see Verification.

MSRV: Rust 1.89.0, the floor shared across the noyalib family and the maintainer’s other Rust crates (noyalib ADR-0014). The toolchain-driven floor is lower: criterion 0.8 (the benchmark dev-dependency) declares rust-version = 1.86, so cargo check --all-targets and the bench suite fail on 1.85 (criterion@0.8.2 requires rustc 1.86), though cargo check --lib still builds. clap_builder 4.6 in the CLI dep tree is edition-2024. We publish the number we verify.


Requirements

  • Rust 1.89.0 or newer to build from source: rust-version in the manifest, enforced by the msrv-core CI job on every push.
  • Any tier-1 platform. CI runs the tests on Linux, macOS, and Windows with the stable, beta, and nightly toolchains; stable is the gate, beta and nightly are early warning.
  • The matching core. This crate pins noyalib at the identical =0.0.X and releases in lockstep with it; Cargo resolves that pin for you.
  • Nothing at runtime: the binaries are static where the platform allows (musl on Linux) and link only the C runtime elsewhere.

Quick Start

# 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.yaml

noyafmt

YAML 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 --check

The formatter runs through noyalib’s lossless CST: comments, anchor positions, and document structure are preserved byte-for-byte; only whitespace and quoting are normalised.

FlagEffect
--checkVerify each FILE is formatted; print files that need formatting; exit 1 if any do. Non-destructive.
--writeRewrite each FILE in place. Default is to print to stdout. Mutually exclusive with --check.
--stdinRead from stdin, write to stdout. Mutually exclusive with FILE arguments.
--indent NIndentation width in spaces, 1 to 16 (default: 2).

noyavalidate

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                    # stdin

The 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.
FlagEffect
-s, --schema PATHValidate each document against JSON Schema 2020-12 at PATH (the schema may itself be YAML or JSON).
--fixRewrite 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, --quietSuppress success output.

GitHub Action and pre-commit

One step in CI, no toolchain on the runner:

- uses: sebastienrousseau/noya-cli@v0.0.58
  with:
    paths: config/ deploy.yaml
    schema: schema.json   # optional

The action downloads the release binaries for the runner’s platform and verifies their build provenance attestation with gh attestation verify (built by this repository’s release workflow at the requested tag); where the gh CLI or a token is missing it checks only the SHA-256 published in the same release and says so with a warning. It then runs noyafmt --check and noyavalidate (with the schema when given) over the YAML under paths; directories are searched for *.yaml and *.yml. Either failure fails the step, and tool output cannot issue workflow commands. version must be X.Y.Z.

The same checks as hosted pre-commit hooks:

repos:
  - repo: https://github.com/sebastienrousseau/noya-cli
    rev: v0.0.58
    hooks:
      - id: noyafmt-check
      - id: noyavalidate

noyafmt (runs noyafmt --write, so the hook fails when it reformatted a file), noyafmt-check and noyavalidate (checks every staged file) are the three hook ids; pass --schema schema.json through args to validate. pre-commit builds the hooks with cargo install without --locked, so dependencies resolve to their newest compatible versions rather than this repository’s Cargo.lock. For a locked build, run cargo install noya-cli --locked and point a repo: local hook with language: system at the installed binaries.

Exit codes

Codenoyafmtnoyavalidate
0success (or no changes if --check)all valid
1parse / I/O error, or --check found unformatted file(s)parse error or schema violation
2invalid usage (bad arg combination)invalid usage
3—I/O error (read / write)

Examples

End-to-end runnable demos under examples/:

ScriptWhat it shows
format-precommit.shDrop-in git pre-commit hook gating commits on noyafmt --check.
validate-k8s.shCI step that runs noyavalidate --schema over a directory of Kubernetes manifests.
fix-quoted-numbers.shWalkthrough 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.sh

Shell completions and man pages

Tarball 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/, …).

make install places all of them in the standard locations. If installing via cargo install, the pre-built copies live in this repository (complete/, docs/*.1) — or regenerate them from the clap definitions:

git clone https://github.com/sebastienrousseau/noya-cli
cd noya-cli
make assets    # regenerates complete/* and docs/*.1 via build.rs

CI enforces that the committed copies are bit-identical to what the clap definitions generate (make check-assets), so they cannot drift from --help.


Verification

Every release artefact (crate archive, SBOM) ships with a cosign keyless signature and a SLSA build-provenance attestation. Verify before trusting a download:

COSIGN_EXPERIMENTAL=1 cosign verify-blob \
  --certificate-identity-regexp '^https://github\.com/sebastienrousseau/noya-cli/\.github/workflows/release\.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  --bundle <artefact>.bundle \
  <artefact>

gh attestation verify <artefact> --repo sebastienrousseau/noya-cli --signer-workflow sebastienrousseau/noya-cli/.github/workflows/release.yml

Full cookbook including the offline / FIPS-bound flow: pkg/VERIFY.md.


When not to use these tools

  • You need to format YAML faster than human-perceivable latency. noyafmt runs end-to-end on a 1 MiB document in ~10 ms; for <100 KB documents that’s already invisible. But for a streaming editor pipeline that wants per-keystroke formatting, the LSP server (noyalib-lsp) issues incremental TextEdit[]s instead.
  • You need YAML 1.1-only behaviour, top to bottom. noyavalidate follows YAML 1.2; the legacy_booleans opt-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 noyalib library directly — every CLI feature flows through public library APIs (cst::format_with_config, validate_against_schema, coerce_to_schema).

Documentation

The four entry points, identical across every repo in the family:


Conformance

Every push runs the official yaml-test-suite through noyavalidate’s exit code, from the same vendored suite and the same core commit as the noyalib core: 406 of 406 (valid cases exit 0, invalid cases exit non-zero). A two-document configuration that uses most of YAML at once (tests/fixtures/ultra-complex/) validates, and noyafmt’s output parses to exactly the same JSON. Details and the family table: noyalib.com/conformance.

License

Dual-licensed under Apache 2.0 or MIT, at your option.