From 1923e93403585e32632216f460b1bc8c5bb39d53 Mon Sep 17 00:00:00 2001 From: Lucas Machado Date: Wed, 6 May 2026 20:26:28 +0200 Subject: [PATCH 1/4] feat(ci): emit GitHub Actions annotations and step summary - Track per-assertion source line via yaml.Node walk in discovery - Add SourceFile / SourceLine on Spec, SpecResult, AssertionResult - New services/fmt_ghactions.go emits ::error file=...,line=...:: lines - Add --github-annotations flag (auto-on when GITHUB_ACTIONS=true) - Reusable workflow writes EMD output to $GITHUB_STEP_SUMMARY - Reusable workflow passes --github-annotations to validate - Integration tests for failing-assertion and passing-suite cases --- .github/workflows/reusable.yml | 16 +++++ actions/validate.go | 35 +++++++---- domain/config.go | 5 ++ domain/models.go | 7 +++ domain/results.go | 24 ++++---- flags.go | 5 ++ integration/validate_test.go | 82 +++++++++++++++++++++++++ main.go | 1 + services/assertion.go | 5 +- services/discovery.go | 76 +++++++++++++++++++++++ services/fmt_ghactions.go | 106 +++++++++++++++++++++++++++++++++ services/runner.go | 7 ++- 12 files changed, 341 insertions(+), 28 deletions(-) create mode 100644 services/fmt_ghactions.go diff --git a/.github/workflows/reusable.yml b/.github/workflows/reusable.yml index acd1496..14e990f 100644 --- a/.github/workflows/reusable.yml +++ b/.github/workflows/reusable.yml @@ -121,11 +121,27 @@ jobs: ARGS="$ARGS --json-output yamlspec-results.json" ARGS="$ARGS --emd-output yamlspec-results.emd.md" ARGS="$ARGS --junit-output yamlspec-results.xml" + # GITHUB_ACTIONS=true auto-enables --github-annotations via env binding, + # but we pass it explicitly for clarity in the run log. + ARGS="$ARGS --github-annotations" ARGS="$ARGS ${{ inputs.extra-args }}" echo "Running: yamlspec $ARGS" yamlspec $ARGS && echo "success=true" >> "$GITHUB_OUTPUT" || echo "success=false" >> "$GITHUB_OUTPUT" + # --- Step summary (visible on the workflow run page) --- + + - name: Write results to step summary + if: always() + run: | + if [ -f yamlspec-results.emd.md ]; then + cat yamlspec-results.emd.md >> "$GITHUB_STEP_SUMMARY" + else + echo "## yamlspec" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo ":warning: Results file not produced — check the run logs." >> "$GITHUB_STEP_SUMMARY" + fi + # --- Upload artifacts --- - name: Upload test results diff --git a/actions/validate.go b/actions/validate.go index 7c3da8b..e4d9ab5 100644 --- a/actions/validate.go +++ b/actions/validate.go @@ -27,18 +27,19 @@ func NewValidateAction() *ValidateAction { // Execute runs test specs func (a *ValidateAction) Execute(c *cli.Context) error { config := &domain.Config{ - TestDir: c.String("test-dir"), - Tags: c.StringSlice("tag"), - Workers: c.Int("workers"), - FailFast: c.Bool("fail-fast"), - PreRunTimeout: c.Duration("pre-run-timeout"), - Verbose: c.GlobalBool("verbose"), - Quiet: c.Bool("quiet"), - JSONOutput: c.String("json-output"), - YAMLOutput: c.String("yaml-output"), - MarkdownOutput: c.String("markdown-output"), - EMDOutput: c.String("emd-output"), - JUnitOutput: c.String("junit-output"), + TestDir: c.String("test-dir"), + Tags: c.StringSlice("tag"), + Workers: c.Int("workers"), + FailFast: c.Bool("fail-fast"), + PreRunTimeout: c.Duration("pre-run-timeout"), + Verbose: c.GlobalBool("verbose"), + Quiet: c.Bool("quiet"), + JSONOutput: c.String("json-output"), + YAMLOutput: c.String("yaml-output"), + MarkdownOutput: c.String("markdown-output"), + EMDOutput: c.String("emd-output"), + JUnitOutput: c.String("junit-output"), + GitHubAnnotations: c.Bool("github-annotations"), } if config.FailFast && config.Workers > 1 { @@ -86,6 +87,16 @@ func (a *ValidateAction) Execute(c *cli.Context) error { } } + // GitHub Actions annotations on stdout — picked up by the runner and + // rendered inline in the PR diff view. + if config.GitHubAnnotations { + gha := services.NewGitHubAnnotationsFormatter() + out, err := gha.Format(result) + if err == nil && len(out) > 0 { + fmt.Print(string(out)) + } + } + if !result.Summary.Success { return cli.NewExitError("", 1) } diff --git a/domain/config.go b/domain/config.go index 48fc4c4..68d06ab 100644 --- a/domain/config.go +++ b/domain/config.go @@ -18,6 +18,11 @@ type Config struct { MarkdownOutput string EMDOutput string JUnitOutput string + + // GitHubAnnotations emits ::error file=...,line=...:: lines on stdout for + // failing assertions, so they appear inline in GitHub PR diffs. Auto-enabled + // when GITHUB_ACTIONS=true. + GitHubAnnotations bool } // DefaultConfig returns a Config with sensible defaults diff --git a/domain/models.go b/domain/models.go index d7bb81c..66cd11f 100644 --- a/domain/models.go +++ b/domain/models.go @@ -6,6 +6,9 @@ type Spec struct { Tags []string `yaml:"tags,omitempty"` PreRun []string `yaml:"pre_run,omitempty"` Describe []DescBlock `yaml:"describe"` + + // SourceFile is the spec.yaml path. Populated by discovery; not parsed from YAML. + SourceFile string `yaml:"-"` } // HasTag returns true if the spec has at least one of the given tags @@ -32,6 +35,10 @@ type Assertion struct { Should string `yaml:"should"` Expect string `yaml:"expect"` + // SourceLine is the 1-indexed line of this assertion's `should:` in spec.yaml. + // Populated by discovery; not parsed from YAML. + SourceLine int `yaml:"-"` + // Equality ToEqual interface{} `yaml:"toEqual,omitempty"` ToNotEqual interface{} `yaml:"toNotEqual,omitempty"` diff --git a/domain/results.go b/domain/results.go index 0c14e73..a8d6c4a 100644 --- a/domain/results.go +++ b/domain/results.go @@ -20,12 +20,13 @@ type SuiteResult struct { // SpecResult is the result of one spec.yaml file type SpecResult struct { - Name string `json:"name" yaml:"name"` - Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"` - Status Status `json:"status" yaml:"status"` - Duration time.Duration `json:"duration" yaml:"duration"` - Describes []DescribeResult `json:"describes,omitempty" yaml:"describes,omitempty"` - Error string `json:"error,omitempty" yaml:"error,omitempty"` + Name string `json:"name" yaml:"name"` + Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"` + Status Status `json:"status" yaml:"status"` + Duration time.Duration `json:"duration" yaml:"duration"` + Describes []DescribeResult `json:"describes,omitempty" yaml:"describes,omitempty"` + Error string `json:"error,omitempty" yaml:"error,omitempty"` + SourceFile string `json:"source_file,omitempty" yaml:"source_file,omitempty"` } // DescribeResult is the result of one describe block @@ -38,11 +39,12 @@ type DescribeResult struct { // AssertionResult is the result of a single assertion type AssertionResult struct { - Should string `json:"should" yaml:"should"` - Status Status `json:"status" yaml:"status"` - Expected interface{} `json:"expected,omitempty" yaml:"expected,omitempty"` - Actual interface{} `json:"actual,omitempty" yaml:"actual,omitempty"` - Error string `json:"error,omitempty" yaml:"error,omitempty"` + Should string `json:"should" yaml:"should"` + Status Status `json:"status" yaml:"status"` + Expected interface{} `json:"expected,omitempty" yaml:"expected,omitempty"` + Actual interface{} `json:"actual,omitempty" yaml:"actual,omitempty"` + Error string `json:"error,omitempty" yaml:"error,omitempty"` + SourceLine int `json:"source_line,omitempty" yaml:"source_line,omitempty"` } // Summary aggregates counts across the run diff --git a/flags.go b/flags.go index f6a458f..b1b6390 100644 --- a/flags.go +++ b/flags.go @@ -58,4 +58,9 @@ var ( Name: "junit-output", Usage: "Write JUnit XML results to file", } + githubAnnotationsFlag = cli.BoolFlag{ + Name: "github-annotations", + Usage: "Emit ::error file=...,line=...:: lines on stdout for failing assertions (auto-enabled when GITHUB_ACTIONS=true)", + EnvVar: "GITHUB_ACTIONS", + } ) diff --git a/integration/validate_test.go b/integration/validate_test.go index c53eb94..8ec5d9f 100644 --- a/integration/validate_test.go +++ b/integration/validate_test.go @@ -1022,3 +1022,85 @@ describe: t.Errorf("pre-run timeout didn't cancel: took %s", elapsed) } } + +// --- GitHub Actions annotations --- + +func TestE2E_GitHubAnnotations_FailingAssertions(t *testing.T) { + bin := buildBinary(t) + tmpDir := t.TempDir() + specDir := filepath.Join(tmpDir, "annotated") + manifestsDir := filepath.Join(specDir, "manifests") + if err := os.MkdirAll(manifestsDir, 0755); err != nil { + t.Fatal(err) + } + + // Note line numbers below; the assertion `should:` lines are referenced. + spec := `name: "Annotated" +tags: ["annotated"] +describe: + - name: "Deployment" + select: 'select(.kind == "Deployment")' + it: + - should: "have 99 replicas" + expect: spec.replicas + toEqual: 99 + - should: "be in fictional namespace" + expect: metadata.namespace + toEqual: "nonexistent" +` + specPath := filepath.Join(specDir, "spec.yaml") + if err := os.WriteFile(specPath, []byte(spec), 0644); err != nil { + t.Fatal(err) + } + + manifest := `apiVersion: apps/v1 +kind: Deployment +metadata: + name: foo + namespace: production +spec: + replicas: 1 +` + if err := os.WriteFile(filepath.Join(manifestsDir, "deployment.yaml"), []byte(manifest), 0644); err != nil { + t.Fatal(err) + } + + output, exitCode := run(t, bin, "validate", "--test-dir", tmpDir, "--github-annotations") + + if exitCode != 1 { + t.Errorf("expected exit 1 (failures), got %d\n%s", exitCode, output) + } + + // Both failing assertions should produce ::error lines pointing at spec.yaml + if !strings.Contains(output, "::error file=") { + t.Errorf("expected ::error annotations in output, got:\n%s", output) + } + if !strings.Contains(output, "spec.yaml") { + t.Errorf("expected spec.yaml in annotation paths, got:\n%s", output) + } + // Line numbers for the two `should:` lines (7 and 10 in the spec above). + for _, frag := range []string{"line=7", "line=10"} { + if !strings.Contains(output, frag) { + t.Errorf("expected %q in annotations, got:\n%s", frag, output) + } + } + // Both assertion descriptions should appear in the annotation messages. + for _, frag := range []string{"have 99 replicas", "be in fictional namespace"} { + if !strings.Contains(output, frag) { + t.Errorf("expected %q in annotations, got:\n%s", frag, output) + } + } +} + +func TestE2E_GitHubAnnotations_PassingSuiteEmitsNothing(t *testing.T) { + bin := buildBinary(t) + + output, exitCode := run(t, bin, "validate", "--test-dir", "integration/testdata", "--tag", "service", "--github-annotations") + + if exitCode != 0 { + t.Errorf("expected exit 0 for passing suite, got %d\n%s", exitCode, output) + } + if strings.Contains(output, "::error") { + t.Errorf("expected no ::error lines for a passing suite, got:\n%s", output) + } +} diff --git a/main.go b/main.go index 0762a38..1512bec 100644 --- a/main.go +++ b/main.go @@ -57,6 +57,7 @@ func main() { markdownOutputFlag, emdOutputFlag, junitOutputFlag, + githubAnnotationsFlag, }, Action: validateAction.Execute, }, diff --git a/services/assertion.go b/services/assertion.go index a274949..d6fb20b 100644 --- a/services/assertion.go +++ b/services/assertion.go @@ -74,8 +74,9 @@ func (ae *AssertionEngine) evaluateDescribe(desc *domain.DescBlock, manifests [] func (ae *AssertionEngine) evaluateAssertion(assertion *domain.Assertion, resources []interface{}) domain.AssertionResult { result := domain.AssertionResult{ - Should: assertion.Should, - Status: domain.StatusPassed, + Should: assertion.Should, + Status: domain.StatusPassed, + SourceLine: assertion.SourceLine, } fieldPath := assertion.Expect diff --git a/services/discovery.go b/services/discovery.go index 2ff1176..4627e9c 100644 --- a/services/discovery.go +++ b/services/discovery.go @@ -155,6 +155,12 @@ func (ds *DiscoveryService) parseSpec(path string) (*domain.Spec, error) { spec.Name = filepath.Base(filepath.Dir(path)) } + spec.SourceFile = path + if err := annotateSourceLines(&spec, data); err != nil { + // Line annotations are best-effort — failure here shouldn't block validation. + helpers.Log.Debug().Err(err).Str("path", path).Msg("could not annotate source lines") + } + if err := validateSpec(&spec); err != nil { return nil, err } @@ -162,6 +168,76 @@ func (ds *DiscoveryService) parseSpec(path string) (*domain.Spec, error) { return &spec, nil } +// annotateSourceLines re-decodes the YAML as a Node tree and stamps each +// assertion's SourceLine. Used for emitting GitHub Actions annotations that +// link back to the right line in spec.yaml. +func annotateSourceLines(spec *domain.Spec, data []byte) error { + var root yaml.Node + if err := yaml.Unmarshal(data, &root); err != nil { + return err + } + if len(root.Content) == 0 { + return nil + } + doc := root.Content[0] + if doc.Kind != yaml.MappingNode { + return nil + } + + describeNode := findMapValue(doc, "describe") + if describeNode == nil || describeNode.Kind != yaml.SequenceNode { + return nil + } + + for di, descNode := range describeNode.Content { + if di >= len(spec.Describe) || descNode.Kind != yaml.MappingNode { + continue + } + itNode := findMapValue(descNode, "it") + if itNode == nil || itNode.Kind != yaml.SequenceNode { + continue + } + for ai, assertNode := range itNode.Content { + if ai >= len(spec.Describe[di].It) || assertNode.Kind != yaml.MappingNode { + continue + } + // Prefer the line of `should:` itself; fall back to the mapping's start. + line := assertNode.Line + if shouldNode := findMapKey(assertNode, "should"); shouldNode != nil { + line = shouldNode.Line + } + spec.Describe[di].It[ai].SourceLine = line + } + } + return nil +} + +// findMapValue returns the value node for the given key in a MappingNode, or nil. +func findMapValue(m *yaml.Node, key string) *yaml.Node { + if m.Kind != yaml.MappingNode { + return nil + } + for i := 0; i+1 < len(m.Content); i += 2 { + if m.Content[i].Value == key { + return m.Content[i+1] + } + } + return nil +} + +// findMapKey returns the key node for the given key in a MappingNode, or nil. +func findMapKey(m *yaml.Node, key string) *yaml.Node { + if m.Kind != yaml.MappingNode { + return nil + } + for i := 0; i+1 < len(m.Content); i += 2 { + if m.Content[i].Value == key { + return m.Content[i] + } + } + return nil +} + // validateSpec ensures the spec is structurally sound. // It catches empty describes, missing fields, and assertions with no operators // set — all of which would otherwise silently pass. diff --git a/services/fmt_ghactions.go b/services/fmt_ghactions.go new file mode 100644 index 0000000..f10a9eb --- /dev/null +++ b/services/fmt_ghactions.go @@ -0,0 +1,106 @@ +package services + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/AxeForging/yamlspec/domain" +) + +// GitHubAnnotationsFormatter emits GitHub Actions workflow commands +// (::error file=...,line=...,title=...::message) for failing assertions and +// errored specs. When written to stdout in a workflow, GitHub renders these +// inline in the PR "Files changed" view. +// +// Spec format: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message +type GitHubAnnotationsFormatter struct{} + +// NewGitHubAnnotationsFormatter creates a new GitHubAnnotationsFormatter. +func NewGitHubAnnotationsFormatter() *GitHubAnnotationsFormatter { + return &GitHubAnnotationsFormatter{} +} + +func (f *GitHubAnnotationsFormatter) Format(result *domain.SuiteResult) ([]byte, error) { + cwd, _ := os.Getwd() + + var b strings.Builder + for _, spec := range result.Specs { + path := relPath(cwd, spec.SourceFile) + + if spec.Error != "" { + fmt.Fprintf(&b, "::error file=%s,title=yamlspec: %s::%s\n", + escapePath(path), escapeProp(spec.Name), escapeMsg(spec.Error)) + continue + } + + for _, desc := range spec.Describes { + for _, ar := range desc.Assertions { + if ar.Status != domain.StatusFailed && ar.Status != domain.StatusError { + continue + } + title := fmt.Sprintf("yamlspec: %s — %s", spec.Name, desc.Name) + msg := fmt.Sprintf("%s: %s", ar.Should, ar.Error) + if ar.Error == "" { + msg = ar.Should + } + + if ar.SourceLine > 0 && path != "" { + fmt.Fprintf(&b, "::error file=%s,line=%d,title=%s::%s\n", + escapePath(path), ar.SourceLine, escapeProp(title), escapeMsg(msg)) + } else if path != "" { + fmt.Fprintf(&b, "::error file=%s,title=%s::%s\n", + escapePath(path), escapeProp(title), escapeMsg(msg)) + } else { + fmt.Fprintf(&b, "::error title=%s::%s\n", escapeProp(title), escapeMsg(msg)) + } + } + } + } + return []byte(b.String()), nil +} + +// relPath returns specPath relative to cwd. Falls back to specPath unchanged +// if Rel fails or specPath is empty. +func relPath(cwd, specPath string) string { + if specPath == "" { + return "" + } + if cwd == "" { + return specPath + } + rel, err := filepath.Rel(cwd, specPath) + if err != nil { + return specPath + } + return rel +} + +// escapeProp escapes workflow-command property values. +// See https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#example-using-properties +func escapeProp(s string) string { + r := strings.NewReplacer( + "%", "%25", + "\r", "%0D", + "\n", "%0A", + ":", "%3A", + ",", "%2C", + ) + return r.Replace(s) +} + +// escapeMsg escapes workflow-command message bodies. +func escapeMsg(s string) string { + r := strings.NewReplacer( + "%", "%25", + "\r", "%0D", + "\n", "%0A", + ) + return r.Replace(s) +} + +// escapePath is a path-safe variant of escapeProp — keeps slashes intact. +func escapePath(s string) string { + return escapeProp(s) +} diff --git a/services/runner.go b/services/runner.go index 1369e74..c7f8bf0 100644 --- a/services/runner.go +++ b/services/runner.go @@ -51,9 +51,10 @@ func (rs *RunnerService) RunAll(ctx context.Context, specs []DiscoveredSpec, con func (rs *RunnerService) RunOne(ctx context.Context, spec DiscoveredSpec, config *domain.Config) *domain.SpecResult { start := time.Now() result := &domain.SpecResult{ - Name: spec.Spec.Name, - Tags: spec.Spec.Tags, - Status: domain.StatusPassed, + Name: spec.Spec.Name, + Tags: spec.Spec.Tags, + Status: domain.StatusPassed, + SourceFile: spec.Spec.SourceFile, } // Execute pre_run commands From ff0902fe2fea37e4df7df6995bfe7a6b45a68422 Mon Sep 17 00:00:00 2001 From: Lucas Machado Date: Wed, 6 May 2026 20:27:51 +0200 Subject: [PATCH 2/4] docs: comprehensive docs and README refresh - Add docs/index.md as docs landing page with cross-links - Add docs/comparison.md vs helm-unittest, ct, conftest, kyverno, datree, kubeconform - Add docs/recipes.md cookbook (multi-env, security, image pinning, labels, HPA/PDB, etc.) - Add docs/troubleshooting.md for common authoring/runtime errors - Add examples/README.md describing each example and run command - Add CONTRIBUTING.md (dev setup, layer conventions, release flow) - Add CHANGELOG.md (Keep a Changelog format) bootstrapped with 0.1.0 - README: badges, sample output block, field-path quick-ref - README: cross-links to docs and examples - README: honest install instructions (no tagged release yet) - Back-link spec-format.md and reusable-workflow.md to docs index --- CHANGELOG.md | 83 +++++++++++ CONTRIBUTING.md | 171 +++++++++++++++++++++ README.md | 83 ++++++++++- docs/comparison.md | 182 +++++++++++++++++++++++ docs/index.md | 30 ++++ docs/recipes.md | 306 ++++++++++++++++++++++++++++++++++++++ docs/reusable-workflow.md | 3 + docs/spec-format.md | 3 + docs/troubleshooting.md | 184 +++++++++++++++++++++++ examples/README.md | 106 +++++++++++++ 10 files changed, 1148 insertions(+), 3 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 docs/comparison.md create mode 100644 docs/index.md create mode 100644 docs/recipes.md create mode 100644 docs/troubleshooting.md create mode 100644 examples/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e77a9b4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,83 @@ +# Changelog + +All notable changes to yamlspec are documented here. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added +- `docs/index.md` documentation landing page with cross-links between docs. +- `docs/comparison.md` covering yamlspec vs `helm unittest`, `chart-testing`, + `conftest`/OPA, Kyverno, Datree, and `kubeconform`. +- `docs/recipes.md` cookbook of common assertion patterns + (multi-environment, security baseline, image pinning, label conventions, + HPA/PDB invariants, NetworkPolicy, etc.). +- `docs/troubleshooting.md` with the common authoring/runtime errors and how + to read them. +- `examples/README.md` describing each worked example and the exact command + to run it. +- `CONTRIBUTING.md` covering dev setup, layer conventions, how to add an + operator/formatter/command, commit conventions, and the release flow. +- README field-path quick reference (dotted, `[index]`, `[*]` wildcard, + bracket notation, leading-dot JQ form) and cross-links to the docs. +- GitHub Actions reusable workflow now writes the EMD output to + `$GITHUB_STEP_SUMMARY` so results show on the workflow run page, not only + as a PR comment. +- `--github-annotations` output mode (auto-enabled when `GITHUB_ACTIONS=true`) + emits `::error file=...,line=...::` lines so failing assertions appear + inline in the GitHub "Files changed" diff view. + +### Changed +- README install instructions now reflect that no GitHub release exists yet — + use `go install` from source until v0.1.0 is cut. + +## [0.1.0] — Initial release + +The first usable yamlspec build, including a correctness-hardening pass. + +### Added +- RSpec-like `describe`/`it`/`should` test syntax in `spec.yaml`. +- 22 assertion operators: equality (`toEqual`, `toNotEqual`), numeric + comparison (`toBeGreaterThan`/`Less`/`OrEqual`), string + (`toContain`/`Start`/`End`/`Match` and negations), existence (`toExist`, + `toBeNull`), object (`toHaveKey`), set membership + (`toBeOneOf`/`toNotBeOneOf`), array (`toContainItem`, `toHaveLength`, + `toHaveMinLength`, `toHaveMaxLength`). +- Wildcard array iteration in field paths + (`spec.containers[*].image`) — assertion runs against every element. +- Distinction between "field missing" and "field is null" for + `toExist`/`toBeNull` semantics. +- Six output formats: console (colored RSpec-style tree), JSON, YAML, + Markdown, enriched Markdown (collapsible `
` for PR comments), + JUnit XML. +- Parallel execution via `--workers N`. +- `--fail-fast` for fast feedback on sequential runs. +- Configurable `--pre-run-timeout` (default 60s) for `pre_run` shell + commands. +- Tag filtering via repeatable `--tag` flag. +- Strict YAML decoding for `spec.yaml` — typos in field names are rejected + rather than silently ignored. +- Comprehensive spec validation: empty `describe`, missing `should`/`expect`, + assertions with no operator are caught at parse time. +- `pre_run` execution kills the entire process group on timeout — orphaned + `sleep`-style children no longer keep the command alive past its deadline. +- Cross-platform process management (Unix `setpgid` + `kill -PGID`; Windows + fallback). +- Reusable GitHub Actions workflow with one-line opt-in via + `uses: AxeForging/yamlspec/.github/workflows/reusable.yml@main`. Posts + enriched-markdown PR comments and updates them in place on subsequent + pushes. +- `init` command to scaffold new specs. +- `list` / `list --tags` for spec/tag discovery (deterministic ordering). +- `ai-help` command emitting a comprehensive reference for AI assistants. +- Built on Go 1.25.8 (covers the recent stdlib CVE). + +### Validation +- 118 tests passing across 6 packages. +- Examples for plain manifests, Helm charts, Kustomize overlays, + multi-resource specs, and a security baseline. + +[Unreleased]: https://github.com/AxeForging/yamlspec/compare/v0.1.0...HEAD +[0.1.0]: https://github.com/AxeForging/yamlspec/releases/tag/v0.1.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..4458767 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,171 @@ +# Contributing + +Thanks for considering a contribution. yamlspec aims to stay small, fast, and +opinionated — pull requests that keep it that way are very welcome. + +## Dev setup + +Requirements: + +- Go 1.25+ (the minimum tracks the upstream stdlib security floor) +- `golangci-lint` for `make lint` +- [`lefthook`](https://github.com/evilmartians/lefthook) for pre-commit/pre-push hooks (optional but recommended) +- `helm` and/or `kustomize` if you want to run those examples locally + +```bash +git clone git@github.com:AxeForging/yamlspec.git +cd yamlspec +go mod download +make build-local # produces ./yamlspec +make test # runs unit + integration tests +``` + +Install hooks (optional): + +```bash +lefthook install +``` + +This wires up `gofmt`, `go vet`, `golangci-lint`, `structlint`, and +conventional-commits message validation on `pre-commit`, plus `govulncheck` on +`pre-push`. + +## Project layout + +``` +yamlspec/ +├── main.go # CLI entry point (urfave/cli v1) +├── flags.go # CLI flag definitions +├── actions/ # Command handlers (validate, list, init, version, ai-help) +├── domain/ # Models (Spec, Results, Config) — no business logic +├── services/ # Business logic +│ ├── assertion.go # Assertion engine (operator evaluation, field paths) +│ ├── discovery.go # Spec file discovery + strict YAML decode +│ ├── runner.go # Test execution (sequential + parallel) +│ ├── runner_unix.go # Unix process-group handling for pre_run timeouts +│ ├── runner_windows.go +│ ├── formatter.go # Formatter interface +│ └── fmt_*.go # Output formatters +├── helpers/ # Utilities (errors, logger, terminal detection) +├── integration/ # E2E tests with testdata fixtures +├── examples/ # Worked examples +└── docs/ # Public-facing documentation +``` + +Keep the layers honest: `actions/` orchestrates, `services/` does the work, +`domain/` is data only. New cross-cutting code generally belongs in +`services/`. + +## Common contribution patterns + +### Adding a new assertion operator + +1. Add the field to `domain.Assertion` in `domain/models.go`. Pointer types + (`*float64`, `*int`, `*bool`) for "not set" / "set to zero" disambiguation. +2. If it's value-based, add it to `Assertion.HasValueOperators()`. If it can + stand alone (like `toExist` / `toBeNull`), wire it into `HasAnyOperator()`. +3. Add the evaluation logic to `services/assertion.go` `evaluateOperators()` — + each operator gets its own short helper. +4. Add unit tests in `services/assertion_test.go`. Cover: passing case, + failing case, type mismatch, missing field. +5. Update [docs/spec-format.md](docs/spec-format.md) and the README operator + table. + +### Adding a new output format + +1. Create `services/fmt_.go` implementing the `Formatter` interface + (single method: `Format(*SuiteResult) ([]byte, error)`). +2. Add a constructor `NewXFormatter()`. +3. Wire it into the `formatters` map in `actions/validate.go`. +4. Add the CLI flag in `flags.go` and reference it from `main.go`. +5. Add an integration test in `integration/`. + +### Adding a new CLI command + +1. Create `actions/.go` with a struct + `Execute(c *cli.Context)` + method. +2. Add a constructor `NewXAction()`. +3. Wire it into the `app.Commands` slice in `main.go`. + +## Testing + +```bash +make test # everything (unit + integration) +make test-unit # services/ only — fast +make test-e2e # integration/ — builds the binary first +make test-coverage # writes coverage.html +``` + +Integration tests live in `integration/` and use `testdata/` fixtures. They +build the binary from source and run it as a subprocess against real spec +files, so they catch regressions in the actual CLI surface, not just the +library code. + +## Linting and formatting + +```bash +make lint # golangci-lint +gofmt -l -w . # auto-format (or let lefthook do it) +``` + +Pre-commit hooks (via `lefthook install`) catch these automatically. + +## Commit conventions + +Conventional Commits are enforced on the commit message. Format: + +``` +[optional scope][!]: +``` + +Allowed types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`, +`build`, `ci`, `perf`, `revert`. + +Examples: + +``` +feat: add toContainItem operator +fix(assertion): handle nil values in toEqual +docs: add troubleshooting guide +test: add integration tests for tag filtering +``` + +Use `!` after the type/scope to mark a breaking change: + +``` +feat!: rename --test-dir to --suite-dir +``` + +## Pull requests + +- Keep changes focused. One feature or fix per PR. +- Update tests. New code without tests will not be merged. +- Update docs. If you change user-facing behavior, update the README, the + relevant `docs/*.md` page, and `CHANGELOG.md` under `## [Unreleased]`. +- The CI workflow (`.github/workflows/pr.yml`) runs lint, tests, and a + govulncheck. All must pass. + +## Releasing + +Releases are cut via the workflow_dispatch `release` workflow: + +1. Land all changes on `main`. Make sure `CHANGELOG.md` has a populated + `## [Unreleased]` section. +2. Run the **Release** workflow from the GitHub Actions tab. Optional `tag` + input — if omitted, the patch version auto-bumps. +3. The workflow runs tests, creates the tag, and runs GoReleaser, which + publishes platform binaries (`linux/darwin/windows × amd64/arm64`) and a + checksums file. +4. After the release lands, move the `## [Unreleased]` entries under a new + `## [vX.Y.Z]` heading in `CHANGELOG.md` and commit. + +GoReleaser config lives in `.goreleaser.yml`. + +## Code of conduct + +Be respectful, give constructive feedback, assume good intent. Standard stuff. + +## Questions? + +Open an issue, or start a draft PR with a question — early feedback beats a +finished PR going the wrong direction. diff --git a/README.md b/README.md index dc7c0db..ed96237 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,14 @@ # yamlspec +[![CI](https://github.com/AxeForging/yamlspec/actions/workflows/pr.yml/badge.svg)](https://github.com/AxeForging/yamlspec/actions/workflows/pr.yml) +[![Go Reference](https://pkg.go.dev/badge/github.com/AxeForging/yamlspec.svg)](https://pkg.go.dev/github.com/AxeForging/yamlspec) +[![Go Report Card](https://goreportcard.com/badge/github.com/AxeForging/yamlspec)](https://goreportcard.com/report/github.com/AxeForging/yamlspec) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) + YAML test framework with RSpec-like assertions. Validate any YAML manifests — Kubernetes, Helm, Kustomize, or plain files — with a clean, readable syntax. +**Documentation:** [docs index](docs/index.md) · [spec.yaml format](docs/spec-format.md) · [recipes](docs/recipes.md) · [troubleshooting](docs/troubleshooting.md) · [vs other tools](docs/comparison.md) · [CI workflow](docs/reusable-workflow.md) + ## Why yamlspec? - **RSpec-like syntax** — `describe`/`it`/`should` vocabulary developers already know @@ -14,15 +21,28 @@ YAML test framework with RSpec-like assertions. Validate any YAML manifests — ## Install +No tagged release yet — install from source: + ```bash -# From source -go install github.com/AxeForging/yamlspec@latest +# Latest commit on main +go install github.com/AxeForging/yamlspec@main + +# Or clone and build +git clone https://github.com/AxeForging/yamlspec.git +cd yamlspec +make install # builds and installs to /usr/local/bin +``` + +Once v0.1.0 is published you'll also be able to use: -# Or download a release +```bash +# Direct download (post-release) curl -sSL https://github.com/AxeForging/yamlspec/releases/latest/download/yamlspec-linux-amd64.tar.gz | tar xz sudo mv yamlspec /usr/local/bin/ ``` +See [CHANGELOG.md](CHANGELOG.md) for release status. + ## Quick Start ```bash @@ -42,6 +62,36 @@ yamlspec validate --json-output results.json --junit-output results.xml yamlspec validate --workers 4 ``` +## Sample output + +``` + ✓ Production deployment + Deployment configuration + [select: select(.kind == "Deployment")] + ✓ have 3 replicas + ✓ use a pinned image tag + ✓ have resource limits + ✓ be in production namespace + + ✗ Staging overlay + Deployment + [select: select(.kind == "Deployment")] + ✓ be in staging namespace + ✗ have 2 replicas + +Failures: + + 1) Staging overlay > Deployment > have 2 replicas + expected 2, got 1 + +26 assertions, 25 passed, 1 failed +Finished in 0.18s +``` + +In CI, the same run also produces a JUnit XML, an enriched-Markdown summary +(rendered as a GitHub PR comment and on the workflow run page), and inline +`::error file=...,line=...::` annotations against `spec.yaml` lines. + ## Usage with Helm Charts Use `pre_run` to render templates before validation: @@ -108,6 +158,8 @@ tests/my-feature/ ## spec.yaml +Full schema reference: [docs/spec-format.md](docs/spec-format.md). + Tests are defined in `spec.yaml` files with an RSpec-like vocabulary: ```yaml @@ -150,6 +202,20 @@ tests/ values.yaml # Optional: Helm values for pre_run ``` +## Field paths + +The `expect:` field accepts these syntaxes — mix freely: + +```yaml +expect: spec.replicas # dotted path +expect: spec.template.spec.containers[0].image # array index +expect: spec.template.spec.containers[*].image # wildcard — assertion runs against every element +expect: metadata.labels["app.kubernetes.io/name"] # bracket notation for keys with dots/special chars +expect: .spec.replicas # leading dot (JQ-style) also works +``` + +Wildcards (`[*]`) iterate every element — useful for "every container must have resource limits" style checks. See [docs/spec-format.md](docs/spec-format.md#field-path-syntax) for the full reference. + ## Assertion Operators | Operator | Example | Description | @@ -212,12 +278,15 @@ VALIDATE FLAGS: --tag, -t Filter by tag (repeatable) --workers, -w Parallel workers (default: 1) --fail-fast Stop on first failure + --pre-run-timeout Max duration for each pre_run command (default: 60s) --quiet, -q Summary only --json-output JSON output file --yaml-output YAML output file --markdown-output Markdown output file --emd-output Enriched markdown output file --junit-output JUnit XML output file + --github-annotations Emit ::error file=...,line=... for failing assertions + (auto-enabled when GITHUB_ACTIONS=true) ``` ## CI/CD — Reusable Workflow @@ -241,8 +310,14 @@ jobs: This installs yamlspec, runs your specs, generates JSON/EMD/JUnit artifacts, and posts a collapsible results comment on PRs. See [docs/reusable-workflow.md](docs/reusable-workflow.md) for all options. +## Examples + +Five runnable examples live under [`examples/`](examples) — see the [examples README](examples/README.md) for what each demonstrates and the exact command to run it. + ## Development +See [CONTRIBUTING.md](CONTRIBUTING.md) for the full guide. Quick reference: + ```bash make build-local # Build binary make test # Run all tests @@ -252,6 +327,8 @@ make lint # Linter make install # Install to /usr/local/bin ``` +Release history is in [CHANGELOG.md](CHANGELOG.md). + ## License MIT diff --git a/docs/comparison.md b/docs/comparison.md new file mode 100644 index 0000000..f1e4414 --- /dev/null +++ b/docs/comparison.md @@ -0,0 +1,182 @@ +# yamlspec vs other tools + +There's no shortage of tools that touch YAML/Kubernetes/Helm validation, and +they overlap in confusing ways. Here's an honest read on where yamlspec fits +and when something else is the better pick. + +## TL;DR + +| Tool | Primary purpose | Pick it over yamlspec when | +|------|-----------------|----------------------------| +| **yamlspec** | Per-environment behavioral assertions on rendered YAML | You want RSpec-style "this deployment, in this overlay, should have these properties" | +| [helm unittest](https://github.com/helm-unittest/helm-unittest) | Unit testing Helm templates | You're a chart author and want to test the templating logic itself | +| [chart-testing (`ct`)](https://github.com/helm/chart-testing) | Lint + install + smoke-test Helm charts | You want a real cluster to actually `helm install` your chart and confirm it comes up | +| [conftest](https://www.conftest.dev/) / [OPA](https://www.openpolicyagent.org/) | Policy-as-code with Rego | You already use Rego elsewhere or need policies enforced across many tool boundaries (admission controllers, Terraform, Dockerfiles) | +| [kyverno](https://kyverno.io/) | Cluster admission policy | You want runtime enforcement, not pre-merge testing | +| [datree](https://www.datree.io/) | Built-in best-practice rules + custom policies | You want a curated rule library out of the box | +| [kubeconform](https://github.com/yannh/kubeconform) | Schema validation against Kubernetes API | You only need "is this valid Kubernetes YAML" — not behavioral assertions | + +## The detailed reads + +### vs `helm unittest` + +`helm unittest` tests **template logic**. yamlspec tests **rendered output**. + +```yaml +# helm unittest — asserts what the template did +asserts: + - equal: + path: spec.replicas + value: 3 + template: deployment.yaml + set: + replicaCount: 3 +``` + +```yaml +# yamlspec — asserts what the rendered manifest looks like in a real values context +pre_run: + - helm template app ../../chart -f values-prod.yaml > manifests/rendered.yaml +describe: + - name: "Deployment" + select: 'select(.kind == "Deployment")' + it: + - should: "have 3 replicas in production" + expect: spec.replicas + toEqual: 3 +``` + +**Pick `helm unittest` when:** you're a chart author and you want fast, +isolated tests of the templating itself — "if I set this value, the template +emits that output." + +**Pick yamlspec when:** you maintain a chart-consumer repo (or your own chart ++ values) and want to assert "given my actual production values, the result +has the properties production needs." It also works on plain manifests and +Kustomize, which `helm unittest` does not. + +You can use both. They answer different questions. + +### vs `chart-testing` (`ct`) + +`ct` lints chart structure (Chart.yaml, README, version bumps), then optionally +spins up a kind cluster and `helm install`s the chart to confirm pods come up. +That's a smoke test of the chart's runtime behavior. + +yamlspec is a static check against rendered YAML. No cluster required, runs in +seconds, and checks **what's in the manifest** rather than **whether it +deploys**. + +**Pick `ct` when:** you want CI to catch "this chart no longer installs" or +"this chart needs a version bump." + +**Pick yamlspec when:** you want to catch "production values dropped resource +limits" or "the staging overlay forgot the env label" before the manifest ever +reaches a cluster. + +These are complementary — many repos run both. + +### vs `conftest` / OPA / Rego + +conftest runs Rego policies against any structured input. It's strictly more +powerful than yamlspec — Rego can do things yamlspec can't. + +The trade-off is the language. Rego has a steep learning curve, error messages +are notoriously cryptic, and a simple "deployment must have 3 replicas" check +takes more lines and more cognitive overhead than the yamlspec equivalent. + +```rego +# conftest +deny[msg] { + input.kind == "Deployment" + input.spec.replicas != 3 + msg := sprintf("expected 3 replicas, got %d", [input.spec.replicas]) +} +``` + +```yaml +# yamlspec +- should: "have 3 replicas" + expect: spec.replicas + toEqual: 3 +``` + +**Pick conftest when:** you're already running Rego policies elsewhere +(Terraform, admission control, Dockerfile linting), or you need logic that +genuinely requires a real policy language — multi-resource correlations, set +algebra, complex string parsing. + +**Pick yamlspec when:** the team isn't going to invest in learning Rego, and +your assertions are mostly "this field should equal/match/contain this value." +Spec files are readable by anyone who's written a unit test in any language. + +### vs Kyverno + +Kyverno is a cluster admission controller. It rejects bad manifests at the +apiserver, generates resources, mutates incoming requests. It's a runtime +control plane, not a CI tool. + +You can run Kyverno policies against YAML files via the `kyverno` CLI, but the +policy syntax is shaped by its admission-control origins — the YAML is more +verbose than yamlspec for equivalent checks. + +**Pick Kyverno when:** you want runtime enforcement (the cluster rejects +non-conformant resources). yamlspec doesn't replace Kyverno; they operate at +different stages. + +**Pick yamlspec when:** you want pre-merge feedback in a PR. You can use +yamlspec's checks as a pre-image of your Kyverno policies — catch violations +before they hit the cluster, then let Kyverno enforce at runtime. + +### vs Datree + +Datree ships a curated library of best-practice rules ("don't use latest tag", +"set resource limits", etc.) plus the ability to write custom policies in +JSONSchema or Rego. + +If your goal is "I want a sane default rule set without writing anything +myself," Datree is the fast path. yamlspec doesn't ship policies — you author +them yourself, though [examples/security-checks](../examples/security-checks) +is a usable starting baseline. + +**Pick Datree when:** you want managed best-practice rules out of the box and +don't want to hand-write policies. + +**Pick yamlspec when:** you have specific, environment-dependent assertions +that don't map to generic best-practice rules. "Production has 3 replicas, +staging has 1, both are in the right namespace" is a yamlspec test, not a +Datree rule. + +### vs `kubeconform` / `kubeval` + +These check "is this valid Kubernetes YAML according to the OpenAPI schema?" +That's structural validation — type/required-field checking — not behavioral. + +**Pick kubeconform when:** you want to catch "I typoed `containrs` instead of +`containers`" or "this field doesn't exist in 1.29." It's fast and cheap and +should run in every CI pipeline that produces manifests. + +**Pick yamlspec when:** the YAML is valid but the *content* might be wrong. +Schema-valid manifests can still set `replicas: 1` in production or use +`:latest` images. + +These are layered: run kubeconform for structure, yamlspec for behavior. + +## When yamlspec is *not* the right answer + +Be honest with yourself: + +- **You need to enforce policies at admission time** — use Kyverno or OPA + Gatekeeper. +- **You need to test that a chart actually deploys** — use `chart-testing` or + hit a kind cluster. +- **You need a curated, maintained rule set with zero authoring effort** — + use Datree. +- **You want to test templating logic in isolation as a chart author** — use + `helm unittest`. +- **You need cross-tool policy consistency** (Terraform + K8s + + Dockerfiles + …) — use Rego/conftest. + +yamlspec's sweet spot is the case those tools handle awkwardly: **per-environment +behavioral assertions on rendered YAML, written in a syntax developers can read +in 30 seconds**. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..4ca1e73 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,30 @@ +# yamlspec documentation + +A YAML test framework with RSpec-like assertions. Validate plain YAML, Helm +charts, Kustomize overlays, or anything else that produces YAML. + +If you're new, start with the project [README](../README.md) for install and a +60-second tour. + +## Reference + +| Page | What's in it | +|------|--------------| +| [spec-format.md](spec-format.md) | Full `spec.yaml` schema — fields, field-path syntax, all 22 assertion operators, selector examples | +| [recipes.md](recipes.md) | Cookbook of common patterns: multi-environment Helm values, security policy checks, label conventions, ConfigMap data, image pinning | +| [troubleshooting.md](troubleshooting.md) | Common failure modes and how to read the error messages | +| [comparison.md](comparison.md) | Honest comparison vs `helm unittest`, `chart-testing`, `conftest`, `kyverno`, `datree`, `kubeconform` | +| [reusable-workflow.md](reusable-workflow.md) | GitHub Actions reusable workflow — one-line CI integration with PR comments | + +## Project meta + +| Page | What's in it | +|------|--------------| +| [../CONTRIBUTING.md](../CONTRIBUTING.md) | Dev setup, how to add an operator or output format, release process | +| [../CHANGELOG.md](../CHANGELOG.md) | Release history (Keep a Changelog format) | + +## Examples + +Five worked examples live under [`../examples/`](../examples) — see the +[examples README](../examples/README.md) for what each one demonstrates and the +exact command to run it. diff --git a/docs/recipes.md b/docs/recipes.md new file mode 100644 index 0000000..28b4184 --- /dev/null +++ b/docs/recipes.md @@ -0,0 +1,306 @@ +# Recipes + +Patterns that come up over and over. Copy, adapt, run. + +For the full schema and operator list see [spec-format.md](spec-format.md). + +## Multi-environment Helm values + +One spec per environment, each rendering against its own values file. +Filesystem layout: + +``` +tests/ + default/ + spec.yaml + staging/ + spec.yaml + values.yaml + production/ + spec.yaml + values.yaml +``` + +```yaml +# tests/production/spec.yaml +name: "Production overrides" +tags: ["production"] + +pre_run: + - helm template prod ../../chart -f values.yaml > manifests/rendered.yaml + +describe: + - name: "Deployment" + select: 'select(.kind == "Deployment")' + it: + - should: "scale up for prod traffic" + expect: spec.replicas + toBeGreaterOrEqual: 3 + + - should: "land in the prod namespace" + expect: metadata.namespace + toEqual: "production" +``` + +Run all environments at once with `yamlspec validate --test-dir tests`, or +filter with `--tag production`. + +## Every container has resource limits (wildcard iteration) + +`[*]` runs the assertion against every element. Failure on any one fails the +assertion. + +```yaml +- should: "every container has CPU limits" + expect: spec.template.spec.containers[*].resources.limits.cpu + toExist: true + +- should: "every container has memory limits" + expect: spec.template.spec.containers[*].resources.limits.memory + toExist: true + +- should: "no container uses :latest" + expect: spec.template.spec.containers[*].image + toNotEndWith: ":latest" +``` + +This is one of the most useful patterns — multi-container pods (sidecars, +init containers) silently regressing is a common production incident. + +## Image pinning with semver + +```yaml +- should: "image is pinned to a semver tag" + expect: spec.template.spec.containers[0].image + toMatch: ':v?\d+\.\d+\.\d+(-[a-z0-9.-]+)?$' + toNotEndWith: ":latest" +``` + +For SHA-pinned images: + +```yaml +- should: "image is pinned to a digest" + expect: spec.template.spec.containers[0].image + toMatch: '@sha256:[a-f0-9]{64}$' +``` + +## Required labels and annotations (the standard k8s set) + +```yaml +- name: "Standard recommended labels" + select: 'select(.kind == "Deployment")' + it: + - should: "have app.kubernetes.io/name" + expect: metadata.labels["app.kubernetes.io/name"] + toExist: true + + - should: "have app.kubernetes.io/instance" + expect: metadata.labels["app.kubernetes.io/instance"] + toExist: true + + - should: "have app.kubernetes.io/version" + expect: metadata.labels["app.kubernetes.io/version"] + toMatch: '^v?\d+\.\d+\.\d+' + + - should: "have app.kubernetes.io/managed-by" + expect: metadata.labels["app.kubernetes.io/managed-by"] + toBeOneOf: ["Helm", "kustomize", "argocd"] +``` + +Bracket notation is required for keys containing dots or slashes. + +## ConfigMap data validation + +```yaml +- name: "App configuration" + select: 'select(.kind == "ConfigMap" and .metadata.name == "app-config")' + it: + - should: "set the right environment" + expect: data.APP_ENV + toEqual: "production" + + - should: "use a valid log level" + expect: data.LOG_LEVEL + toBeOneOf: ["debug", "info", "warn", "error"] + + - should: "not enable debug in prod" + expect: data.DEBUG + toNotEqual: "true" + + - should: "have a sane request timeout" + expect: data.REQUEST_TIMEOUT_MS + toMatch: '^\d+$' +``` + +## Pod security baseline + +```yaml +- name: "Pod security context" + select: 'select(.kind == "Deployment")' + it: + - should: "run as non-root" + expect: spec.template.spec.securityContext.runAsNonRoot + toEqual: true + + - should: "set a non-root UID" + expect: spec.template.spec.securityContext.runAsUser + toBeGreaterThan: 0 + + - should: "disable service account token automount" + expect: spec.template.spec.automountServiceAccountToken + toEqual: false + +- name: "Container security context" + select: 'select(.kind == "Deployment")' + it: + - should: "every container has a read-only root filesystem" + expect: spec.template.spec.containers[*].securityContext.readOnlyRootFilesystem + toEqual: true + + - should: "every container drops all capabilities" + expect: spec.template.spec.containers[*].securityContext.capabilities.drop + toContainItem: "ALL" + + - should: "no container allows privilege escalation" + expect: spec.template.spec.containers[*].securityContext.allowPrivilegeEscalation + toEqual: false +``` + +## HPA / PDB invariants + +```yaml +- name: "HorizontalPodAutoscaler" + select: 'select(.kind == "HorizontalPodAutoscaler")' + it: + - should: "have min replicas in [3, 10]" + expect: spec.minReplicas + toBeGreaterOrEqual: 3 + toBeLessOrEqual: 10 + + - should: "have max replicas <= 50" + expect: spec.maxReplicas + toBeLessOrEqual: 50 + +- name: "PodDisruptionBudget" + select: 'select(.kind == "PodDisruptionBudget")' + it: + - should: "guarantee at least 2 pods stay available" + expect: spec.minAvailable + toBeGreaterOrEqual: 2 +``` + +## NetworkPolicy egress rules + +```yaml +- name: "Default-deny + allow-list" + select: 'select(.kind == "NetworkPolicy")' + it: + - should: "include Egress in policy types" + expect: spec.policyTypes + toContainItem: "Egress" + + - should: "include Ingress in policy types" + expect: spec.policyTypes + toContainItem: "Ingress" + + - should: "have at least one egress rule (not pure deny-all)" + expect: spec.egress + toHaveMinLength: 1 +``` + +## Service / Ingress sanity + +```yaml +- name: "Service" + select: 'select(.kind == "Service" and .metadata.name == "web")' + it: + - should: "be ClusterIP (not LoadBalancer in this env)" + expect: spec.type + toEqual: "ClusterIP" + + - should: "expose exactly one port" + expect: spec.ports + toHaveLength: 1 + + - should: "select pods by app label" + expect: spec.selector.app + toEqual: "web" + +- name: "Ingress" + select: 'select(.kind == "Ingress")' + it: + - should: "use TLS" + expect: spec.tls + toHaveMinLength: 1 + + - should: "use the prod ingress class" + expect: spec.ingressClassName + toEqual: "nginx-prod" +``` + +## Cross-overlay assertion (Kustomize) + +```yaml +# tests/staging/spec.yaml +pre_run: + - kustomize build ../../overlays/staging > manifests/rendered.yaml + +describe: + - name: "Staging-specific overrides" + select: 'select(.kind == "Deployment" and .metadata.name == "api")' + it: + - should: "use the staging image registry" + expect: spec.template.spec.containers[0].image + toStartWith: "registry.staging.example.com/" + + - should: "have the env label patched in" + expect: metadata.labels.env + toEqual: "staging" +``` + +## Multi-document manifests + +yamlspec parses every `---`-separated document in every YAML file under +`manifests/`. No special config needed: + +```yaml +# manifests/rendered.yaml (output of helm template or kustomize build) +apiVersion: v1 +kind: Service +... +--- +apiVersion: apps/v1 +kind: Deployment +... +--- +apiVersion: v1 +kind: ConfigMap +... +``` + +Each `describe` block uses its `select:` to pick which documents the +assertions apply to. + +## Combining multiple operators + +Operators on a single assertion are AND'd — all must pass: + +```yaml +- should: "image is on our registry, semver-tagged, and not :latest" + expect: spec.template.spec.containers[0].image + toStartWith: "registry.example.com/" + toMatch: ':v\d+\.\d+\.\d+$' + toNotEndWith: ":latest" +``` + +For OR semantics, write multiple assertions or use `toBeOneOf`. + +## Reusable security baseline + +The [`examples/security-checks`](../examples/security-checks) spec is a +ready-to-run baseline you can drop into any chart or kustomize repo as +`tests/security/spec.yaml`. + +```bash +yamlspec validate --test-dir tests --tag security +``` diff --git a/docs/reusable-workflow.md b/docs/reusable-workflow.md index c78aab5..4dd68d8 100644 --- a/docs/reusable-workflow.md +++ b/docs/reusable-workflow.md @@ -1,5 +1,8 @@ # Reusable Workflow +[← back to docs index](index.md) + + yamlspec provides a reusable GitHub Actions workflow that any repo can call to run tests and post results as PR comments. ## Quick Start diff --git a/docs/spec-format.md b/docs/spec-format.md index 285f0b5..06404fc 100644 --- a/docs/spec-format.md +++ b/docs/spec-format.md @@ -1,5 +1,8 @@ # spec.yaml Format Reference +[← back to docs index](index.md) + + ## Structure ```yaml diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..4530396 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,184 @@ +# Troubleshooting + +Common failure modes and how to read what yamlspec is telling you. + +## "spec must contain at least one describe block" + +``` +Error: discovery failed: parse '.../tests/foo/spec.yaml': spec must contain at least one describe block +``` + +Your `spec.yaml` parsed cleanly but has no `describe:` entries (or the field +is missing entirely). Add at least one `describe` block with at least one +`it` assertion. + +## "describe[0] (\"X\").it[0] (\"Y\") has no assertion operator" + +``` +describe[0] ("Deployment").it[2] ("be in production namespace") has no assertion operator +(add one of toEqual, toExist, toContain, etc.) +``` + +The assertion has a `should:` and `expect:` but no operator like `toEqual:`. +This is the most common authoring mistake — easy to forget the operator line. +Add one (or more): + +```yaml +- should: "be in production namespace" + expect: metadata.namespace + toEqual: "production" # ← was missing +``` + +## "field X not found in type domain.Spec" + +``` +Error: yaml: unmarshal errors: + line 7: field describes not found in type domain.Spec +``` + +yamlspec uses **strict YAML decoding** — typos in field names are rejected, +not silently ignored. Fix the typo (`describes` → `describe`, `pre-run` → +`pre_run`, etc.). The valid top-level fields are `name`, `tags`, `pre_run`, +`describe`. + +## "selector 'X' matched no resources" + +``` +Status: FAILED +Error: selector 'select(.kind == "Deployment")' matched no resources +``` + +The JQ selector ran fine but matched zero documents. Possible causes: + +1. **No manifests were discovered** — yamlspec looks for YAML in + `/manifests/` first, falling back to YAML files alongside + `spec.yaml` (excluding `spec.yaml` and `values.yaml` themselves). Confirm + the files exist and have a `.yaml` or `.yml` extension. + +2. **`pre_run` ran but didn't write where you expected** — make sure your + redirect targets `manifests/`: + + ```yaml + pre_run: + - mkdir -p manifests + - helm template app ../../chart > manifests/rendered.yaml + ``` + +3. **The selector itself is wrong** — typo in the kind, namespace mismatch, + etc. Try a broader selector first (`select: ""` matches everything) and + narrow from there. + +## "pre_run failed: command 'X' timed out after Ys" + +``` +Status: ERROR +Error: pre_run failed: command 'helm template ...' timed out after 1m0s +``` + +The default per-command timeout is 60 seconds. Bump it for slow renders: + +```bash +yamlspec validate --pre-run-timeout 5m +``` + +If a command was hanging on a child process (e.g. `sleep` left over from a +test), the timeout now kills the entire process group — no orphaned children. + +## "no manifests found" + +``` +Status: ERROR +Error: no manifests found +``` + +The spec ran its `pre_run` (if any) but yamlspec found zero YAML files to +test. Check: + +- Files have `.yaml` / `.yml` extension (not `.txt`, not extension-less) +- If using `manifests/` subdirectory, the directory exists and contains files +- If your `pre_run` writes to a different path, point yamlspec at the right + directory by writing into `manifests/` + +## "field path not found" vs "field is null" + +These are different. yamlspec distinguishes: + +| Situation | `toExist: true` | `toExist: false` | `toBeNull: true` | `toBeNull: false` | +|-----------|-----------------|------------------|------------------|-------------------| +| Field absent | FAIL | PASS | FAIL | FAIL | +| Field present, value `null` | FAIL | PASS | PASS | FAIL | +| Field present, non-null value | PASS | FAIL | FAIL | PASS | + +If you're checking that a field both exists *and* is non-null, `toExist: true` +is what you want. `toBeNull: true` only passes when the field is explicitly +present but null. + +## "--fail-fast cannot be used with --workers > 1" + +``` +Error: --fail-fast cannot be used with --workers > 1 +(parallel workers can't honor ordered short-circuit; pick one) +``` + +These are mutually exclusive — parallel execution can't honor a +deterministic short-circuit. Pick one: + +- `--fail-fast` (sequential, stops on first failure — fast feedback) +- `--workers N` (parallel, runs everything — fast wall time) + +## Empty or malformed spec.yaml + +``` +Error: discovery failed: parse '.../tests/foo/spec.yaml': spec file is empty +``` + +The file was readable but had no content (or only whitespace). Re-scaffold +with `yamlspec init ` if you want a starter template. + +## YAML parsing errors in manifests + +``` +parse '.../manifests/rendered.yaml': yaml: line 42: did not find expected node content +``` + +Your manifests YAML is malformed. If this came from a `pre_run` rendering +step, run that command manually and inspect the output — `helm template` +sometimes emits empty documents or comment-only blocks that confuse strict +parsers. yamlspec uses `gopkg.in/yaml.v3`, which is stricter than `yaml.v2`. + +## Wildcard matched nothing + +A wildcard path like `spec.containers[*].image` over an empty array yields +zero values. yamlspec treats this as "no values to assert against" — your +assertion will pass vacuously for value-based operators (`toEqual`, +`toMatch`, etc.) but `toExist: true` will still fail because the path +produced no values. + +If you want to assert "there is at least one container," add a separate +length check: + +```yaml +- should: "has at least one container" + expect: spec.containers + toHaveMinLength: 1 +``` + +## Verbose mode + +When in doubt, run with `--verbose` to see debug logs (pre_run commands, +discovered specs, etc.): + +```bash +yamlspec --verbose validate --test-dir tests +``` + +## Still stuck? + +Open an issue with: + +- Your `spec.yaml` +- A minimal manifest that reproduces the problem +- The exact command you ran +- The full error output + +The smaller the repro, the faster the fix. diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..ede7b44 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,106 @@ +# Examples + +Five runnable examples covering the main use cases. Each lives in its own +directory and is a complete, self-contained spec — clone the repo and run them +locally to see what passing/failing output looks like. + +## Running an example + +From the repo root, after `make build-local` (binary lands at `./yamlspec`): + +```bash +./yamlspec validate --test-dir examples/ +``` + +Or run all examples at once: + +```bash +./yamlspec validate --test-dir examples +``` + +Filter by tag: + +```bash +./yamlspec validate --test-dir examples --tag security +``` + +## What each example shows + +### [`plain-manifests/`](plain-manifests) + +The simplest case — no `pre_run`, no rendering. Just YAML files in +`manifests/` and a `spec.yaml` next to them. + +Demonstrates: deployment replica count, image-tag policy (no `:latest`, +semver-only), resource limits/requests, health-probe presence, ConfigMap +key/value checks, set-membership (`toBeOneOf` for log levels). + +```bash +./yamlspec validate --test-dir examples/plain-manifests +``` + +### [`helm-chart/`](helm-chart) + +Full Helm pipeline — `pre_run` runs `helm template` against a real chart with +a values override, writes the output to `manifests/rendered.yaml`, then +yamlspec asserts on the rendered result. + +Demonstrates: Helm rendering integration, asserting that `values.yaml` +overrides actually take effect, multi-resource validation in one spec. + +Requires `helm` on `$PATH`. + +```bash +./yamlspec validate --test-dir examples/helm-chart +``` + +### [`kustomize-overlay/`](kustomize-overlay) + +Kustomize equivalent — `pre_run` runs `kustomize build` against a base, asserts +on the built output. + +Demonstrates: kustomize integration, namespace/label patches, replica patches. + +Requires `kustomize` on `$PATH`. + +```bash +./yamlspec validate --test-dir examples/kustomize-overlay +``` + +### [`multi-resource/`](multi-resource) + +Several resources of different kinds (Deployment + Service + HPA + PDB) tested +in a single spec, each isolated via JQ selector. + +Demonstrates: `select: 'select(.kind == "X")'` filtering, asserting +relationships between resources (HPA targets the Deployment, PDB selector +matches the pods), array-length checks (`toHaveLength`, `toHaveMinLength`). + +```bash +./yamlspec validate --test-dir examples/multi-resource +``` + +### [`security-checks/`](security-checks) + +A reusable security baseline — runAsNonRoot, readOnlyRootFilesystem, dropped +capabilities, no `:latest` tags, mandatory probes, mandatory resource limits. + +Demonstrates: how to write a security-policy spec you can drop into any chart +or kustomize repo. No `pre_run` — point it at any rendered manifest with +`./yamlspec validate --test-dir examples/security-checks`. + +This example is also a good template for organization-wide policy checks. + +## Output formats + +Try the different formatters on any example: + +```bash +./yamlspec validate --test-dir examples/multi-resource \ + --json-output results.json \ + --emd-output results.md \ + --junit-output results.xml +``` + +See [docs/spec-format.md](../docs/spec-format.md) for the full assertion +reference and [docs/recipes.md](../docs/recipes.md) for more patterns. From 9d51f2ae9ddb1792658ce91c4065c2a882fdd6c9 Mon Sep 17 00:00:00 2001 From: Lucas Machado Date: Wed, 6 May 2026 20:44:21 +0200 Subject: [PATCH 3/4] docs: add demo GIF to README - Add docs/demo.gif (146KB, ~28s) showing spec, manifest, passing run, broken run - Keep docs/demo.cast for future re-renders - Embed GIF below README intro paragraph - Allowlist *.gif and *.cast in structlint --- .structlint.yaml | 2 + README.md | 2 + docs/demo.cast | 244 +++++++++++++++++++++++++++++++++++++++++++++++ docs/demo.gif | Bin 0 -> 149349 bytes 4 files changed, 248 insertions(+) create mode 100644 docs/demo.cast create mode 100644 docs/demo.gif diff --git a/.structlint.yaml b/.structlint.yaml index fb533f5..2197223 100644 --- a/.structlint.yaml +++ b/.structlint.yaml @@ -33,6 +33,8 @@ file_naming_pattern: - "*.md" - "*.xml" - "*.txt" + - "*.gif" + - "*.cast" - "Makefile" - ".gitignore" - ".goreleaser.yml" diff --git a/README.md b/README.md index ed96237..f3e8ef5 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ YAML test framework with RSpec-like assertions. Validate any YAML manifests — Kubernetes, Helm, Kustomize, or plain files — with a clean, readable syntax. +![yamlspec demo](docs/demo.gif) + **Documentation:** [docs index](docs/index.md) · [spec.yaml format](docs/spec-format.md) · [recipes](docs/recipes.md) · [troubleshooting](docs/troubleshooting.md) · [vs other tools](docs/comparison.md) · [CI workflow](docs/reusable-workflow.md) ## Why yamlspec? diff --git a/docs/demo.cast b/docs/demo.cast new file mode 100644 index 0000000..e850404 --- /dev/null +++ b/docs/demo.cast @@ -0,0 +1,244 @@ +{"version": 2, "width": 100, "height": 28, "timestamp": 1778092906, "idle_time_limit": 3.0, "env": {"SHELL": "/usr/bin/zsh", "TERM": "tmux-256color"}, "title": "yamlspec demo"} +[0.017382, "o", "\u001b[H\u001b[J\u001b[3J"] +[0.519245, "o", "\u001b[2;36m# yamlspec — RSpec-like assertions for any YAML\u001b[0m\r\n"] +[2.021317, "o", "\u001b[2;36m# Define what 'good' looks like in spec.yaml:\u001b[0m\r\n"] +[2.923289, "o", "\u001b[1;32m$\u001b[0m c"] +[2.965548, "o", "a"] +[3.008012, "o", "t"] +[3.050562, "o", " "] +[3.102221, "o", "t"] +[3.154026, "o", "e"] +[3.20557, "o", "s"] +[3.257781, "o", "t"] +[3.30949, "o", "s"] +[3.361744, "o", "/"] +[3.413876, "o", "p"] +[3.465519, "o", "r"] +[3.51744, "o", "o"] +[3.569625, "o", "d"] +[3.621317, "o", "u"] +[3.673569, "o", "c"] +[3.725469, "o", "t"] +[3.776986, "o", "i"] +[3.829255, "o", "o"] +[3.881898, "o", "n"] +[3.934725, "o", "/"] +[3.987241, "o", "s"] +[4.040092, "o", "p"] +[4.078561, "o", "e"] +[4.116327, "o", "c"] +[4.154401, "o", "."] +[4.192575, "o", "y"] +[4.230672, "o", "a"] +[4.269022, "o", "m"] +[4.307314, "o", "l"] +[4.597326, "o", "\r\n"] +[4.59877, "o", "name: \"Production deployment\"\r\ntags: [\"production\", \"deployment\"]\r\n\r\ndescribe:\r\n - name: \"Deployment\"\r\n select: 'select(.kind == \"Deployment\")'\r\n it:\r\n - should: \"have 3 replicas\"\r\n expect: spec.replicas\r\n toEqual: 3\r\n\r\n - should: \"be in production namespace\"\r\n expect: metadata.namespace\r\n toEqual: \"production\"\r\n\r\n - should: \"use a pinned image tag\"\r\n expect: spec.template.spec.containers[0].image\r\n toNotEndWith: \":latest\"\r\n\r\n - should: \"have CPU limits\"\r\n expect: spec.template.spec.containers[0].resources.limits.cpu\r\n toExist: true\r\n"] +[7.6026, "o", "\u001b[H\u001b[J\u001b[3J"] +[7.602851, "o", "\u001b[2;36m# And the manifest we want to validate:\u001b[0m\r\n"] +[8.50459, "o", "\u001b[1;32m$\u001b[0m c"] +[8.563744, "o", "a"] +[8.623326, "o", "t"] +[8.682932, "o", " "] +[8.742304, "o", "t"] +[8.801497, "o", "e"] +[8.861339, "o", "s"] +[8.920428, "o", "t"] +[8.979703, "o", "s"] +[9.038877, "o", "/"] +[9.081938, "o", "p"] +[9.125309, "o", "r"] +[9.169028, "o", "o"] +[9.212399, "o", "d"] +[9.255465, "o", "u"] +[9.299072, "o", "c"] +[9.342282, "o", "t"] +[9.385472, "o", "i"] +[9.428355, "o", "o"] +[9.471569, "o", "n"] +[9.515123, "o", "/"] +[9.55879, "o", "m"] +[9.601586, "o", "a"] +[9.644671, "o", "n"] +[9.687421, "o", "i"] +[9.730464, "o", "f"] +[9.773898, "o", "e"] +[9.817123, "o", "s"] +[9.860338, "o", "t"] +[9.903677, "o", "s"] +[9.947112, "o", "/"] +[9.990527, "o", "d"] +[10.033767, "o", "e"] +[10.077224, "o", "p"] +[10.11736, "o", "l"] +[10.157408, "o", "o"] +[10.197256, "o", "y"] +[10.237481, "o", "m"] +[10.277463, "o", "e"] +[10.317522, "o", "n"] +[10.357653, "o", "t"] +[10.397907, "o", "."] +[10.438188, "o", "y"] +[10.478387, "o", "a"] +[10.518927, "o", "m"] +[10.559493, "o", "l"] +[10.851579, "o", "\r\n"] +[10.853001, "o", "apiVersion: apps/v1\r\nkind: Deployment\r\nmetadata:\r\n name: web\r\n namespace: production\r\nspec:\r\n replicas: 3\r\n selector:\r\n matchLabels:\r\n app: web\r\n template:\r\n metadata:\r\n labels:\r\n app: web\r\n spec:\r\n containers:\r\n - name: web\r\n image: nginx:1.25.3\r\n resources:\r\n limits:\r\n cpu: \"500m\"\r\n memory: \"256Mi\"\r\n"] +[13.856621, "o", "\u001b[H\u001b[J\u001b[3J"] +[13.856868, "o", "\u001b[2;36m# Run the tests:\u001b[0m\r\n"] +[14.758491, "o", "\u001b[1;32m$\u001b[0m ."] +[14.800197, "o", "/"] +[14.842251, "o", "y"] +[14.884455, "o", "a"] +[14.926849, "o", "m"] +[14.969755, "o", "l"] +[15.012345, "o", "s"] +[15.054748, "o", "p"] +[15.099137, "o", "e"] +[15.143491, "o", "c"] +[15.188164, "o", " "] +[15.232539, "o", "v"] +[15.276755, "o", "a"] +[15.321101, "o", "l"] +[15.365347, "o", "i"] +[15.409151, "o", "d"] +[15.453398, "o", "a"] +[15.497029, "o", "t"] +[15.540565, "o", "e"] +[15.5852, "o", " "] +[15.629629, "o", "-"] +[15.67457, "o", "-"] +[15.718674, "o", "t"] +[15.762538, "o", "e"] +[15.806429, "o", "s"] +[15.850778, "o", "t"] +[15.894986, "o", "-"] +[15.938513, "o", "d"] +[15.98282, "o", "i"] +[16.027188, "o", "r"] +[16.071227, "o", " "] +[16.131612, "o", "t"] +[16.191799, "o", "e"] +[16.251748, "o", "s"] +[16.311971, "o", "t"] +[16.372346, "o", "s"] +[16.684761, "o", "\r\n"] +[16.690924, "o", "\r\n \u001b[32m✓\u001b[0m \u001b[1mProduction deployment\u001b[0m\r\n Deployment\r\n \u001b[36m[select: select(.kind == \"Deployment\")]\u001b[0m\r\n \u001b[32m✓\u001b[0m have 3 replicas\r\n \u001b[32m✓\u001b[0m be in production namespace\r\n \u001b[32m✓\u001b[0m use a pinned image tag\r\n \u001b[32m✓\u001b[0m have CPU limits\r\n\r\n\r\n4 assertions, \u001b[32m4 passed\u001b[0m\r\nFinished in 0.00s\r\n\r\n"] +[19.193184, "o", "\u001b[2;36m# Break it — drop replicas to 1:\u001b[0m\r\n"] +[20.095501, "o", "\u001b[1;32m$\u001b[0m s"] +[20.154429, "o", "e"] +[20.214446, "o", "d"] +[20.273673, "o", " "] +[20.332705, "o", "-"] +[20.391701, "o", "i"] +[20.451065, "o", " "] +[20.510616, "o", "'"] +[20.569722, "o", "s"] +[20.62924, "o", "/"] +[20.689011, "o", "r"] +[20.748685, "o", "e"] +[20.807841, "o", "p"] +[20.867684, "o", "l"] +[20.927103, "o", "i"] +[20.986392, "o", "c"] +[21.044952, "o", "a"] +[21.107656, "o", "s"] +[21.169726, "o", ":"] +[21.232243, "o", " "] +[21.294896, "o", "3"] +[21.357125, "o", "/"] +[21.419141, "o", "r"] +[21.481428, "o", "e"] +[21.544104, "o", "p"] +[21.606423, "o", "l"] +[21.668626, "o", "i"] +[21.730607, "o", "c"] +[21.792844, "o", "a"] +[21.854484, "o", "s"] +[21.917117, "o", ":"] +[21.979397, "o", " "] +[22.041778, "o", "1"] +[22.097601, "o", "/"] +[22.154065, "o", "'"] +[22.210159, "o", " "] +[22.266485, "o", "t"] +[22.322307, "o", "e"] +[22.37897, "o", "s"] +[22.434537, "o", "t"] +[22.491311, "o", "s"] +[22.546857, "o", "/"] +[22.603137, "o", "p"] +[22.65952, "o", "r"] +[22.715623, "o", "o"] +[22.771467, "o", "d"] +[22.826911, "o", "u"] +[22.882697, "o", "c"] +[22.937937, "o", "t"] +[22.994003, "o", "i"] +[23.049504, "o", "o"] +[23.107298, "o", "n"] +[23.165433, "o", "/"] +[23.223791, "o", "m"] +[23.281587, "o", "a"] +[23.339826, "o", "n"] +[23.398051, "o", "i"] +[23.456333, "o", "f"] +[23.51511, "o", "e"] +[23.57315, "o", "s"] +[23.630826, "o", "t"] +[23.689381, "o", "s"] +[23.747784, "o", "/"] +[23.806031, "o", "d"] +[23.864387, "o", "e"] +[23.923197, "o", "p"] +[23.981028, "o", "l"] +[24.039514, "o", "o"] +[24.081617, "o", "y"] +[24.124449, "o", "m"] +[24.166406, "o", "e"] +[24.208994, "o", "n"] +[24.251553, "o", "t"] +[24.29365, "o", "."] +[24.334789, "o", "y"] +[24.377155, "o", "a"] +[24.419641, "o", "m"] +[24.461846, "o", "l"] +[24.756435, "o", "\r\n"] +[25.760824, "o", "\u001b[1;32m$\u001b[0m ."] +[25.820645, "o", "/"] +[25.882122, "o", "y"] +[25.942938, "o", "a"] +[26.003745, "o", "m"] +[26.064272, "o", "l"] +[26.112765, "o", "s"] +[26.160711, "o", "p"] +[26.208545, "o", "e"] +[26.2559, "o", "c"] +[26.303597, "o", " "] +[26.351976, "o", "v"] +[26.400069, "o", "a"] +[26.448033, "o", "l"] +[26.495905, "o", "i"] +[26.543907, "o", "d"] +[26.592661, "o", "a"] +[26.640751, "o", "t"] +[26.68903, "o", "e"] +[26.737372, "o", " "] +[26.785714, "o", "-"] +[26.83373, "o", "-"] +[26.882395, "o", "t"] +[26.930264, "o", "e"] +[26.978462, "o", "s"] +[27.026581, "o", "t"] +[27.074896, "o", "-"] +[27.135129, "o", "d"] +[27.195433, "o", "i"] +[27.255473, "o", "r"] +[27.315584, "o", " "] +[27.375216, "o", "t"] +[27.434953, "o", "e"] +[27.494735, "o", "s"] +[27.554574, "o", "t"] +[27.614694, "o", "s"] +[27.926854, "o", "\r\n"] +[27.932694, "o", "\r\n \u001b[31m✗\u001b[0m \u001b[1mProduction deployment\u001b[0m\r\n Deployment\r\n \u001b[36m[select: select(.kind == \"Deployment\")]\u001b[0m\r\n \u001b[31m✗\u001b[0m have 3 replicas\r\n \u001b[2m\u001b[31mexpected 'spec.replicas' to equal 3, got 1\u001b[0m\r\n \u001b[32m✓\u001b[0m be in production namespace\r\n \u001b[32m✓\u001b[0m use a pinned image tag\r\n \u001b[32m✓\u001b[0m have CPU limits\r\n\r\n\r\n\u001b[31m\u001b[1mFailures:\u001b[0m\r\n\r\n \u001b[31m1) Production deployment > Deployment > have 3 replicas\u001b[0m\r\n \u001b[2mexpected 'spec.replicas' to equal 3, got 1\u001b[0m\r\n\r\n\r\n4 assertions, \u001b[32m3 passed\u001b[0m, \u001b[31m1 failed\u001b[0m\r\nFinished in 0.00s\r\n\r\n"] diff --git a/docs/demo.gif b/docs/demo.gif new file mode 100644 index 0000000000000000000000000000000000000000..b3e706e9d97ca66703cab75e05c5f1822a197d8a GIT binary patch literal 149349 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�Ch! zV!(9NG`JH)+h?;tW{@rb2yj&Z2nXn);F~>WLdg@S^&oZ$&>B&K;sKS4?fp7Po3&u(8)bl z^2i1&x6KypC0ieaSDKe zYJjG8%$%_8mw$U=xN}|q9}|ucs(ej=%YXtolNHpU#3D;&i4+J?<72ND+U%H{0CT`$ zhyrjT`{f{T6Y=pPPg4@S;(96qV)8y@DrBQ*r{$NJurDb!Md8=g2<6{=lxkYQMKv20 z3YZySZYPD(^8L7E(0_zmL3}xsi?t#7A7XhScuZ^bvqwyoFgBif-f|@Sp4f-!*0MS z{b$adJBHuwwUb|{CZh%?00XqZOn;yPGyRo~{@HT$#A*1^nDiRX75TF4y6O1-XIHDR zzS+i6X=<6pPfBc5uu>E+Ihne4=`rcijU%KEL!YbCQfa#nvD`K=khKz`i*HM6(vUUo z3_aOLx0aitU_HVAq2P{R)$F2r_is_Wt5MW;8arvSA0Yi(i9WCo^{ftf;dYGew5*|; z4#0pLm_K;_Cot3^cBISp3GQE&=>L8UUnpNekBl#KnV7>nW^2u(ys~r<7s!fUmtE0B z1?R&nR@L$w+(Y}zZM;~zmoHYm`e1kFMEt8C;pdc%qyBNWzJoOiu>Slt2fhe`3y=XI zU>)F{)U`bTqLTgUk^c)U_ie2IepC7Elf8k>D#k=aesqr2E89vs9~Mh#J!(q6Nz_sZ zJax?H(_0JkGLJ723|V)*Ei`)?s;ZWiBPB=?=gx*6^^BGH<%6&S*tYM_|JlZrYAgT- z@Bj?37qHQG1_8~wy!@z%10W&Ya6|Yyb~Sg}dM3xAvj;mZMcgj#+RyepDfNNEEu~$L zzmBl0#@}8`-sd_eUJ@((l6yU~Tyy&d$ob z^xPw9F_UX2b&hlPHcFwk4Q#%qE12QT=6G5`WXA6l_aq2BQACE#df!Z3zPQuS5WcDnAe& z5N!v0M+X1|Ap*jm8aluTfD0rzfKN?%|JA|%q7+kOlqUMtT!sMHKi+g!=?~U?Flf%O#M7P6e}wft1AGpS?Uo7?cmsG z7%79nmi@p(c?sUQJ%a)3T1EvoU4*&yW6S3!i+8nph|r!2PcmqfPBj z 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 literal 0 HcmV?d00001 From 9c41b30d12cf0244be07df6a39d59e0547081ca1 Mon Sep 17 00:00:00 2001 From: Lucas Machado Date: Wed, 6 May 2026 20:50:10 +0200 Subject: [PATCH 4/4] docs: drop comparison.md - Remove docs/comparison.md - Drop references from README, docs/index.md, CHANGELOG.md --- CHANGELOG.md | 2 - README.md | 2 +- docs/comparison.md | 182 --------------------------------------------- docs/index.md | 1 - 4 files changed, 1 insertion(+), 186 deletions(-) delete mode 100644 docs/comparison.md diff --git a/CHANGELOG.md b/CHANGELOG.md index e77a9b4..8fc5c00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `docs/index.md` documentation landing page with cross-links between docs. -- `docs/comparison.md` covering yamlspec vs `helm unittest`, `chart-testing`, - `conftest`/OPA, Kyverno, Datree, and `kubeconform`. - `docs/recipes.md` cookbook of common assertion patterns (multi-environment, security baseline, image pinning, label conventions, HPA/PDB invariants, NetworkPolicy, etc.). diff --git a/README.md b/README.md index f3e8ef5..c7cdd66 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ YAML test framework with RSpec-like assertions. Validate any YAML manifests — ![yamlspec demo](docs/demo.gif) -**Documentation:** [docs index](docs/index.md) · [spec.yaml format](docs/spec-format.md) · [recipes](docs/recipes.md) · [troubleshooting](docs/troubleshooting.md) · [vs other tools](docs/comparison.md) · [CI workflow](docs/reusable-workflow.md) +**Documentation:** [docs index](docs/index.md) · [spec.yaml format](docs/spec-format.md) · [recipes](docs/recipes.md) · [troubleshooting](docs/troubleshooting.md) · [CI workflow](docs/reusable-workflow.md) ## Why yamlspec? diff --git a/docs/comparison.md b/docs/comparison.md deleted file mode 100644 index f1e4414..0000000 --- a/docs/comparison.md +++ /dev/null @@ -1,182 +0,0 @@ -# yamlspec vs other tools - -There's no shortage of tools that touch YAML/Kubernetes/Helm validation, and -they overlap in confusing ways. Here's an honest read on where yamlspec fits -and when something else is the better pick. - -## TL;DR - -| Tool | Primary purpose | Pick it over yamlspec when | -|------|-----------------|----------------------------| -| **yamlspec** | Per-environment behavioral assertions on rendered YAML | You want RSpec-style "this deployment, in this overlay, should have these properties" | -| [helm unittest](https://github.com/helm-unittest/helm-unittest) | Unit testing Helm templates | You're a chart author and want to test the templating logic itself | -| [chart-testing (`ct`)](https://github.com/helm/chart-testing) | Lint + install + smoke-test Helm charts | You want a real cluster to actually `helm install` your chart and confirm it comes up | -| [conftest](https://www.conftest.dev/) / [OPA](https://www.openpolicyagent.org/) | Policy-as-code with Rego | You already use Rego elsewhere or need policies enforced across many tool boundaries (admission controllers, Terraform, Dockerfiles) | -| [kyverno](https://kyverno.io/) | Cluster admission policy | You want runtime enforcement, not pre-merge testing | -| [datree](https://www.datree.io/) | Built-in best-practice rules + custom policies | You want a curated rule library out of the box | -| [kubeconform](https://github.com/yannh/kubeconform) | Schema validation against Kubernetes API | You only need "is this valid Kubernetes YAML" — not behavioral assertions | - -## The detailed reads - -### vs `helm unittest` - -`helm unittest` tests **template logic**. yamlspec tests **rendered output**. - -```yaml -# helm unittest — asserts what the template did -asserts: - - equal: - path: spec.replicas - value: 3 - template: deployment.yaml - set: - replicaCount: 3 -``` - -```yaml -# yamlspec — asserts what the rendered manifest looks like in a real values context -pre_run: - - helm template app ../../chart -f values-prod.yaml > manifests/rendered.yaml -describe: - - name: "Deployment" - select: 'select(.kind == "Deployment")' - it: - - should: "have 3 replicas in production" - expect: spec.replicas - toEqual: 3 -``` - -**Pick `helm unittest` when:** you're a chart author and you want fast, -isolated tests of the templating itself — "if I set this value, the template -emits that output." - -**Pick yamlspec when:** you maintain a chart-consumer repo (or your own chart -+ values) and want to assert "given my actual production values, the result -has the properties production needs." It also works on plain manifests and -Kustomize, which `helm unittest` does not. - -You can use both. They answer different questions. - -### vs `chart-testing` (`ct`) - -`ct` lints chart structure (Chart.yaml, README, version bumps), then optionally -spins up a kind cluster and `helm install`s the chart to confirm pods come up. -That's a smoke test of the chart's runtime behavior. - -yamlspec is a static check against rendered YAML. No cluster required, runs in -seconds, and checks **what's in the manifest** rather than **whether it -deploys**. - -**Pick `ct` when:** you want CI to catch "this chart no longer installs" or -"this chart needs a version bump." - -**Pick yamlspec when:** you want to catch "production values dropped resource -limits" or "the staging overlay forgot the env label" before the manifest ever -reaches a cluster. - -These are complementary — many repos run both. - -### vs `conftest` / OPA / Rego - -conftest runs Rego policies against any structured input. It's strictly more -powerful than yamlspec — Rego can do things yamlspec can't. - -The trade-off is the language. Rego has a steep learning curve, error messages -are notoriously cryptic, and a simple "deployment must have 3 replicas" check -takes more lines and more cognitive overhead than the yamlspec equivalent. - -```rego -# conftest -deny[msg] { - input.kind == "Deployment" - input.spec.replicas != 3 - msg := sprintf("expected 3 replicas, got %d", [input.spec.replicas]) -} -``` - -```yaml -# yamlspec -- should: "have 3 replicas" - expect: spec.replicas - toEqual: 3 -``` - -**Pick conftest when:** you're already running Rego policies elsewhere -(Terraform, admission control, Dockerfile linting), or you need logic that -genuinely requires a real policy language — multi-resource correlations, set -algebra, complex string parsing. - -**Pick yamlspec when:** the team isn't going to invest in learning Rego, and -your assertions are mostly "this field should equal/match/contain this value." -Spec files are readable by anyone who's written a unit test in any language. - -### vs Kyverno - -Kyverno is a cluster admission controller. It rejects bad manifests at the -apiserver, generates resources, mutates incoming requests. It's a runtime -control plane, not a CI tool. - -You can run Kyverno policies against YAML files via the `kyverno` CLI, but the -policy syntax is shaped by its admission-control origins — the YAML is more -verbose than yamlspec for equivalent checks. - -**Pick Kyverno when:** you want runtime enforcement (the cluster rejects -non-conformant resources). yamlspec doesn't replace Kyverno; they operate at -different stages. - -**Pick yamlspec when:** you want pre-merge feedback in a PR. You can use -yamlspec's checks as a pre-image of your Kyverno policies — catch violations -before they hit the cluster, then let Kyverno enforce at runtime. - -### vs Datree - -Datree ships a curated library of best-practice rules ("don't use latest tag", -"set resource limits", etc.) plus the ability to write custom policies in -JSONSchema or Rego. - -If your goal is "I want a sane default rule set without writing anything -myself," Datree is the fast path. yamlspec doesn't ship policies — you author -them yourself, though [examples/security-checks](../examples/security-checks) -is a usable starting baseline. - -**Pick Datree when:** you want managed best-practice rules out of the box and -don't want to hand-write policies. - -**Pick yamlspec when:** you have specific, environment-dependent assertions -that don't map to generic best-practice rules. "Production has 3 replicas, -staging has 1, both are in the right namespace" is a yamlspec test, not a -Datree rule. - -### vs `kubeconform` / `kubeval` - -These check "is this valid Kubernetes YAML according to the OpenAPI schema?" -That's structural validation — type/required-field checking — not behavioral. - -**Pick kubeconform when:** you want to catch "I typoed `containrs` instead of -`containers`" or "this field doesn't exist in 1.29." It's fast and cheap and -should run in every CI pipeline that produces manifests. - -**Pick yamlspec when:** the YAML is valid but the *content* might be wrong. -Schema-valid manifests can still set `replicas: 1` in production or use -`:latest` images. - -These are layered: run kubeconform for structure, yamlspec for behavior. - -## When yamlspec is *not* the right answer - -Be honest with yourself: - -- **You need to enforce policies at admission time** — use Kyverno or OPA - Gatekeeper. -- **You need to test that a chart actually deploys** — use `chart-testing` or - hit a kind cluster. -- **You need a curated, maintained rule set with zero authoring effort** — - use Datree. -- **You want to test templating logic in isolation as a chart author** — use - `helm unittest`. -- **You need cross-tool policy consistency** (Terraform + K8s + - Dockerfiles + …) — use Rego/conftest. - -yamlspec's sweet spot is the case those tools handle awkwardly: **per-environment -behavioral assertions on rendered YAML, written in a syntax developers can read -in 30 seconds**. diff --git a/docs/index.md b/docs/index.md index 4ca1e73..a636731 100644 --- a/docs/index.md +++ b/docs/index.md @@ -13,7 +13,6 @@ If you're new, start with the project [README](../README.md) for install and a | [spec-format.md](spec-format.md) | Full `spec.yaml` schema — fields, field-path syntax, all 22 assertion operators, selector examples | | [recipes.md](recipes.md) | Cookbook of common patterns: multi-environment Helm values, security policy checks, label conventions, ConfigMap data, image pinning | | [troubleshooting.md](troubleshooting.md) | Common failure modes and how to read the error messages | -| [comparison.md](comparison.md) | Honest comparison vs `helm unittest`, `chart-testing`, `conftest`, `kyverno`, `datree`, `kubeconform` | | [reusable-workflow.md](reusable-workflow.md) | GitHub Actions reusable workflow — one-line CI integration with PR comments | ## Project meta