flux-manifest-preview

module
v0.0.0-test10 Latest Latest
Warning

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

Go to latest
Published: Apr 29, 2026 License: GPL-3.0

README ΒΆ

Flux Manifest Preview (fmp)

Build Status GitHub release (latest by date) Go Report Card License

fmp is a CLI tool designed to render and diff the manifests produced by Flux GitOps repositories. It bridges the gap between local development and the cluster by showing exactly what Flux would apply.


πŸš€ Key Features

fmp understands common Flux repository patterns instead of treating YAML as flat files:

  • 🚒 GitOps Awareness: Discover and follow Flux Kustomization.spec.path and resolve external GitRepository sources.
  • πŸ“¦ Helm Integration: Render HelmRelease resources through the Helm SDK, including support for post-renderers and commonMetadata.
  • πŸ” Intelligent Diffing: Compare local worktree changes against HEAD or other revisions with noisy field normalization.
  • πŸ” Secret Support: Decrypt SOPS-encrypted resources on the fly when requested.
  • 🧹 Clean Output: Filter generated fields (like timestamps or random hashes) for deterministic and readable diffs.

[!NOTE] Status: This project is under active development. While it is useful for day-to-day validation and review, expect some rough edges as the workflow is refined.


πŸ’Ύ Install

From source:

go install ./cmd/fmp

Directly via Go:

go install github.com/tobiash/flux-manifest-preview/cmd/fmp@latest
πŸ› οΈ Requirements
  • git: For git-aware diffing and external repository resolution.
  • helm: For registry/cache configuration when rendering Helm charts.

⚑ Quick Start

The core workflow is designed to be git-aware. Run this inside a Flux git worktree to see what would change if you committed your current work:

fmp diff

This compares the rendered output of HEAD against your current dirty worktree.


πŸ› οΈ Usage & Commands

πŸ” Diffing Changes

Compare the rendered output of different git refs or local paths:

fmp diff                      # Diff HEAD vs Worktree
fmp diff HEAD~1               # Diff 1 commit back vs Worktree
fmp diff main feature-branch  # Diff two branches
fmp diff ./before ./after     # Diff two local paths
fmp diff git:HEAD path:/tmp   # Mix git refs and local paths
πŸ“„ Rendering Manifests

Render a repository or a specific path to standard output:

fmp render <path>
fmp render --output json <path>
πŸ§ͺ Validation & Discovery

Ensure your Flux resources are well-formed and discoverable:

fmp test <path>               # Validate that KS/HR can be rendered
fmp get ks <path>             # List discovered Kustomizations
fmp get hr <path>             # List discovered HelmReleases
πŸ€– CI & Advanced Tools

Tools for automation and handling non-deterministic output:

fmp ci                        # CI-optimized diff mode
fmp detect-permadiffs <path>  # Detect noisy fields and generate filters

πŸ€– Agent-Friendly Features

fmp is designed for programmatic consumption by automation and AI agents.

Structured Output (--output json)

All commands that produce YAML or reports support --output json:

fmp diff --output json
fmp render --output json <path>
fmp test --output json <path>
fmp get ks --output json <path>
fmp get hr --output json <path>

JSON output follows Kubernetes API conventions:

  • Lists are wrapped in a v1/List envelope with apiVersion, kind, and items
  • Resource references use ObjectRef (apiVersion, kind, name, namespace)
  • Error responses use a structured envelope: {"status": "failure", "error": {"reason": "...", "message": "..."}}
Semantic Exit Codes
Code Meaning
0 Success (no differences for diff)
1 Differences found (diff only) or generic error
2 User input error (bad args, missing file, invalid config)
3 Dependency failure (Helm chart missing, git error, network)
5 Policy violation
10 Unexpected internal error
Programmatic Discovery (describe)

The hidden describe command emits a JSON description of the CLI for agent consumption:

fmp describe

This includes all commands, flags, descriptions, and subcommand metadata.


βš™οΈ Configuration

fmp auto-discovers configuration from .fmp.yaml, .fmp.yml, or .github/fmp.yaml.

fmp configuration is not just for render settings. It can also define policy rules that:

  • classify diffs (image_update, ingress_change, etc.)
  • fail local diff or GitHub Action runs when specific rules match
  • suggest or apply GitHub pull request labels

Example .fmp.yaml:

paths:
  - clusters/kube
recursive: true
helm: true
resolve-git: true
sort: true
exclude-crds: true
filters:
  - kind: FieldNormalizer
    match:
      kind: Secret
    fieldPaths:
      - path: [data, tls.crt]
        action: replace
        placeholder: "<<auto-generated>>"
policies:
  builtin:
    - image_update
    - ingress_change
  modules:
    - .fmp/policies/*.rego
  inline:
    - |
      package fmp
      import rego.v1

      violations contains {
        "id": "forbid_latest",
        "message": "latest tag is forbidden"
      } if {
        some change in input.changes
        change.new.spec.template.spec.containers[_].image == "nginx:latest"
      }
  fail-on:
    - forbid_latest
  labels:
    image_update: image-update
    ingress_change:
      - needs-network-review
      - risky-change

Note: In diff mode, configuration is loaded from the current worktree so that local config changes take immediate effect.

Policy Rules

Policies run against a structured diff document containing summary counts and per-resource old / new objects.

You can combine:

  • built-in policies shipped with fmp
  • file-based Rego modules via policies.modules
  • inline Rego modules via policies.inline

Built-in policies currently include:

  • image_update: a workload container image changed
  • secret_change: a Secret was added, modified, or deleted
  • ingress_change: an Ingress, Gateway, or HTTPRoute changed
  • crd_change: a CustomResourceDefinition changed
  • namespace_delete: a Namespace was deleted
  • stateful_workload_change: a StatefulSet or DaemonSet changed
  • pvc_change: a PersistentVolumeClaim changed
  • service_type_change: a Service changed spec.type
  • replicas_change: a workload replica count changed

fail-on matches policy IDs. If any listed rule matches, fmp diff exits non-zero and the GitHub Action fails.

labels maps policy IDs to one or more pull request labels.

Minimal example:

policies:
  builtin:
    - image_update
    - ingress_change
  fail-on:
    - forbid_latest
  labels:
    image_update: image-update
    ingress_change:
      - needs-network-review
      - risky-change
  inline:
    - |
      package fmp
      import rego.v1

      violations contains {
        "id": "forbid_latest",
        "message": sprintf("%s/%s uses :latest", [change.namespace, change.name]),
        "severity": "error"
      } if {
        some change in input.changes
        spec := object.get(change.new, "spec", {})
        template := object.get(spec, "template", {})
        podspec := object.get(template, "spec", {})
        some c in object.get(podspec, "containers", [])
        endswith(object.get(c, "image", ""), ":latest")
      }
Local Testing

Use the plain CLI first.

Raw diff only:

fmp diff main

Summary plus raw diff:

fmp diff --summary main

Summary only, no raw diff:

fmp diff --summary-only main

--summary prints the human-oriented summary to stderr and keeps the unified diff on stdout, which is safer for piping and scripting.

Example local workflow:

fmp diff --summary main > /tmp/fmp.diff 2> /tmp/fmp.summary
less /tmp/fmp.summary
less /tmp/fmp.diff

πŸ™ GitHub Action

Use fmp in your CI/CD pipelines to review PRs automatically.

- uses: actions/checkout@v6
  with:
    fetch-depth: 0  # Required for git-aware diffing

- uses: tobiash/flux-manifest-preview@vX.Y.Z
  with:
    repo: .
    base-ref: origin/main
    resolve-git: true
Inputs
Input Description Default
binary Path to an existing fmp executable for local or non-release testing
repo Path to the repo checkout (must be a git worktree) .
base-ref Git ref to diff against origin/main
base-sha Exact SHA to diff against (overrides base-ref)
paths Directories to render (newline-separated)
recursive Recursively discover paths false
helm Enable Helm rendering true
resolve-git Clone external GitRepository sources false
sort Sort output for deterministic diffs false
exclude-crds Strip CRDs from output false
helm-release Filter diff to a specific HelmRelease
config Explicit path to .fmp.yaml auto-discovered
filter-file KIO filter definition file
filter-yaml Raw KIO filters YAML
write-summary Write a step summary true
comment Post/update a sticky PR comment false
comment-mode When to comment (changes, always, failure) changes
max-inline-diff-bytes Max diff bytes to inline 50000
html-report Generate and upload a rich HTML report artifact false
html-report-name HTML report artifact name flux-manifest-preview-report
html-report-retention-days HTML report artifact retention in days 7
html-report-max-resource-diff-bytes Max per-resource diff bytes embedded in the HTML report 2000000
export-dir Export rendered manifests directory
export-changed-only Only export changed manifests false
fail-on-warning Fail the step on warnings false
fail-on-error Fail the step on errors true
Outputs
Output Description
status Overall status: clean, changed, warning, error
changed Whether manifest changes were detected
warnings-count Number of warnings
errors-count Number of errors
resources-added Added resources
resources-modified Modified resources
resources-deleted Deleted resources
resources-total Total changed resources
diff-bytes Size of the full diff
diff-truncated Whether the inline diff was truncated
diff-file Path to the full diff file
summary-file Path to the summary markdown
comment-file Path to the comment markdown
report-file Path to the structured JSON report
html-report-file Path to the generated HTML report index file
html-report-artifact Name of the uploaded HTML report artifact
export-dir Directory where manifests were exported
classifications-json JSON array of matched policy classifications
violations-json JSON array of matched policy violations
labels-json JSON array of suggested or applied PR labels
policy-failed Whether a configured fail-on policy matched
Examples

Opt-in sticky PR comment:

- uses: tobiash/flux-manifest-preview@vX.Y.Z
  with:
    repo: .
    base-ref: origin/main
    comment: true
    comment-mode: changes

Rich HTML report artifact:

- uses: tobiash/flux-manifest-preview@vX.Y.Z
  with:
    repo: .
    base-ref: origin/main
    html-report: true

The HTML report opens with a long-form impact overview, then provides a resource browser with kind, namespace, producer, action, and search filters. Each resource opens a detailed diff view with unified and side-by-side modes.

Export for downstream validation:

- uses: tobiash/flux-manifest-preview@vX.Y.Z
  with:
    repo: .
    base-ref: origin/main
    export-dir: ./rendered
    export-changed-only: true

- name: Validate with kubeconform
  run: kubeconform ./rendered/*.yaml

Preview multiple clusters from one repo:

- uses: tobiash/flux-manifest-preview@vX.Y.Z
  with:
    repo: .
    base-ref: origin/main
    paths: |
      clusters/kube
      clusters/prod
      clusters/edge

Note: sops-decrypt is intentionally unsupported in the GitHub Action to avoid leaking decrypted content into logs, summaries, comments, or artifacts.

For local action development or branch testing, build fmp ahead of time and pass it via binary. The action no longer builds Go code for you.

paths and legacy kustomizations inputs are newline-separated, so pointing the action at one subdirectory per cluster is already supported.

If you want fmp to apply pull request labels directly in GitHub Actions, grant issues: write in addition to pull-requests: write.


πŸ§ͺ Development

Run the test suite and linter:

go test ./...
go vet ./...

Directories ΒΆ

Path Synopsis
cmd
fmp command
pkg

Jump to

Keyboard shortcuts

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