canic-cli 0.68.2

Operator CLI for Canic fleet setup, builds, evidence, catalog, backup, and restore workflows
Documentation
//! Module: evidence::command
//!
//! Responsibility: build `canic evidence` Clap command definitions and usage text.
//! Does not own: command dispatch, option parsing, policy evaluation, or report rendering.
//! Boundary: passive CLI surface construction for evidence commands.

use crate::cli::clap::{passthrough_subcommand, render_usage, value_arg};
use clap::{ArgGroup, Command as ClapCommand};

use super::options::{EvidenceCompareFormat, EvidenceGateFormat};

pub(super) fn usage() -> String {
    render_usage(evidence_command)
}

pub(super) fn compare_usage() -> String {
    render_usage(evidence_compare_command)
}

pub(super) fn gate_usage() -> String {
    render_usage(evidence_gate_command)
}

pub(super) fn evidence_command() -> ClapCommand {
    ClapCommand::new("evidence")
        .bin_name("canic evidence")
        .about("Evaluate and compare stable Canic evidence envelopes")
        .disable_help_flag(true)
        .subcommand(passthrough_subcommand(
            ClapCommand::new("gate")
                .about("Evaluate one EvidenceEnvelopeV1 against a CI policy")
                .disable_help_flag(true),
        ))
        .subcommand(passthrough_subcommand(
            ClapCommand::new("compare")
                .about("Compare two EvidenceEnvelopeV1 JSON files")
                .disable_help_flag(true),
        ))
}

pub(super) fn evidence_gate_command() -> ClapCommand {
    ClapCommand::new("gate")
        .bin_name("canic evidence gate")
        .about("Evaluate one EvidenceEnvelopeV1 against a CI policy")
        .disable_help_flag(true)
        .arg(
            value_arg("policy")
                .long("policy")
                .value_name("path")
                .required(true),
        )
        .arg(
            value_arg("envelope")
                .long("envelope")
                .value_name("path")
                .required(false),
        )
        .arg(
            value_arg("manifest")
                .long("manifest")
                .value_name("path")
                .required(false),
        )
        .arg(
            value_arg("format")
                .long("format")
                .value_name("text|json|envelope-json")
                .default_value("text")
                .value_parser(clap::value_parser!(EvidenceGateFormat)),
        )
        .arg(value_arg("output").long("output").value_name("path"))
        .group(
            ArgGroup::new("gate-input")
                .args(["envelope", "manifest"])
                .required(true)
                .multiple(false),
        )
        .after_help(
            "Examples:\n  canic evidence gate --policy ci/canic-policy.toml --envelope artifacts/canic/build-provenance.json\n  canic evidence gate --policy ci/canic-policy.toml --manifest ci/canic-evidence.toml --format json --output artifacts/canic/policy-gate-report.json\n\nReads exactly one policy file and either one existing EvidenceEnvelopeV1 or one project evidence manifest. The gate is passive: it does not run builds, deploy, discover live state, mutate inputs, or turn policy success into deployment truth.",
        )
}

pub(super) fn evidence_compare_command() -> ClapCommand {
    ClapCommand::new("compare")
        .bin_name("canic evidence compare")
        .about("Compare two EvidenceEnvelopeV1 JSON files")
        .disable_help_flag(true)
        .arg(
            value_arg("left")
                .long("left")
                .value_name("file")
                .required(true),
        )
        .arg(
            value_arg("right")
                .long("right")
                .value_name("file")
                .required(true),
        )
        .arg(
            value_arg("format")
                .long("format")
                .value_name("text|json")
                .default_value("text")
                .value_parser(clap::value_parser!(EvidenceCompareFormat)),
        )
        .after_help(
            "Compares stable envelope fields and ignores generated_at, canic_version, and the nested payload body. The payload_sha256 field is compared.",
        )
}