use std::path::PathBuf;
use clap::{Args, Parser, Subcommand, ValueEnum};
use clap_complete::Shell;
#[derive(Debug, Parser)]
#[command(
name = "envorigin",
version,
about = "Explain where environment variables come from",
long_about = "EnvOrigin builds a provenance chain for environment variables in Docker Compose projects and GitHub Actions workflows: across shell variables, interpolation files, env_file layers, and environment overrides. Values are redacted unless --show-values is passed."
)]
pub struct Cli {
#[command(subcommand)]
pub command: Command,
}
#[derive(Debug, Subcommand)]
pub enum Command {
Scan(ScanArgs),
Explain(ExplainArgs),
Audit(AuditArgs),
Diff(DiffArgs),
Graph(GraphArgs),
Gitlab(GitlabArgs),
Circleci(CircleciArgs),
Actions(ActionsArgs),
Lsp,
Completions(CompletionsArgs),
}
#[derive(Debug, Args)]
pub struct CircleciArgs {
#[command(subcommand)]
pub command: CircleciCommand,
}
#[derive(Debug, Subcommand)]
pub enum CircleciCommand {
Scan(CircleciScanArgs),
Explain(CircleciExplainArgs),
Audit(CircleciAuditArgs),
Graph(CircleciScanArgs),
}
#[derive(Debug, Args)]
pub struct CircleciAuditArgs {
#[command(flatten)]
pub common: CircleciScanArgs,
#[arg(long, value_enum, default_value_t = FailLevel::Error)]
pub fail_on: FailLevel,
#[arg(long)]
pub config: Option<PathBuf>,
}
#[derive(Debug, Args)]
pub struct CircleciScanArgs {
#[arg(long = "file", short = 'f', default_value = ".circleci/config.yml")]
pub file: PathBuf,
#[arg(long)]
pub show_values: bool,
#[arg(long, value_enum, default_value_t = OutputFormat::Human)]
pub format: OutputFormat,
}
#[derive(Debug, Args)]
pub struct CircleciExplainArgs {
pub variable: String,
#[arg(long, short = 'j')]
pub job: String,
#[command(flatten)]
pub common: CircleciScanArgs,
}
#[derive(Debug, Args)]
pub struct GitlabArgs {
#[command(subcommand)]
pub command: GitlabCommand,
}
#[derive(Debug, Subcommand)]
pub enum GitlabCommand {
Scan(GitlabScanArgs),
Explain(GitlabExplainArgs),
Audit(GitlabAuditArgs),
Graph(GitlabScanArgs),
}
#[derive(Debug, Args)]
pub struct GitlabAuditArgs {
#[command(flatten)]
pub common: GitlabScanArgs,
#[arg(long, value_enum, default_value_t = FailLevel::Error)]
pub fail_on: FailLevel,
#[arg(long)]
pub config: Option<PathBuf>,
}
#[derive(Debug, Args)]
pub struct GitlabScanArgs {
#[arg(long = "file", short = 'f', default_value = ".gitlab-ci.yml")]
pub file: PathBuf,
#[arg(long)]
pub show_values: bool,
#[arg(long, value_enum, default_value_t = OutputFormat::Human)]
pub format: OutputFormat,
}
#[derive(Debug, Args)]
pub struct GitlabExplainArgs {
pub variable: String,
#[arg(long, short = 'j')]
pub job: Option<String>,
#[command(flatten)]
pub common: GitlabScanArgs,
}
#[derive(Debug, Args)]
pub struct GraphArgs {
#[command(flatten)]
pub common: CommonArgs,
}
#[derive(Debug, Args)]
pub struct DiffArgs {
#[arg(required = true, num_args = 2..)]
pub files: Vec<PathBuf>,
#[arg(long)]
pub show_values: bool,
#[arg(long, value_enum, default_value_t = OutputFormat::Human)]
pub format: OutputFormat,
}
#[derive(Debug, Args)]
pub struct AuditArgs {
#[command(flatten)]
pub common: CommonArgs,
#[arg(long, value_enum, default_value_t = FailLevel::Error)]
pub fail_on: FailLevel,
#[arg(long)]
pub config: Option<PathBuf>,
}
#[derive(Debug, Args)]
pub struct CompletionsArgs {
pub shell: Shell,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum FailLevel {
None,
Info,
Warning,
Error,
}
impl FailLevel {
pub fn triggers(self, severity: crate::model::Severity) -> bool {
use crate::model::Severity;
use FailLevel::*;
match (self, severity) {
(None, _) => false,
(Error, Severity::Error) => true,
(Warning, Severity::Warning | Severity::Error) => true,
(Info, _) => true,
(_, _) => false,
}
}
}
#[derive(Debug, Args)]
pub struct ActionsArgs {
#[command(subcommand)]
pub command: ActionsCommand,
}
#[derive(Debug, Subcommand)]
pub enum ActionsCommand {
Scan(ActionsScanArgs),
Explain(ActionsExplainArgs),
Audit(ActionsAuditArgs),
Graph(ActionsScanArgs),
}
#[derive(Debug, Args)]
pub struct ActionsAuditArgs {
#[command(flatten)]
pub common: ActionsScanArgs,
#[arg(long, value_enum, default_value_t = FailLevel::Error)]
pub fail_on: FailLevel,
#[arg(long)]
pub config: Option<PathBuf>,
}
#[derive(Debug, Args)]
pub struct ActionsScanArgs {
#[arg(long = "file", short = 'f', default_value = ".github/workflows/ci.yml")]
pub workflow_file: PathBuf,
#[arg(long)]
pub project_directory: Option<PathBuf>,
#[arg(long)]
pub show_values: bool,
#[arg(long, value_enum, default_value_t = OutputFormat::Human)]
pub format: OutputFormat,
}
#[derive(Debug, Args)]
pub struct ActionsExplainArgs {
pub variable: String,
#[arg(long, short = 'j')]
pub job: Option<String>,
#[arg(long, short = 's')]
pub step: Option<String>,
#[command(flatten)]
pub common: ActionsScanArgs,
}
#[derive(Debug, Args)]
pub struct ScanArgs {
#[command(flatten)]
pub common: CommonArgs,
#[arg(long)]
pub service: Option<String>,
}
#[derive(Debug, Args)]
pub struct ExplainArgs {
pub variable: String,
#[arg(long, short = 's')]
pub service: Option<String>,
#[command(flatten)]
pub common: CommonArgs,
}
#[derive(Debug, Args)]
pub struct CommonArgs {
#[arg(long = "file", short = 'f', default_value = "compose.yaml")]
pub compose_file: PathBuf,
#[arg(long = "env-file")]
pub env_files: Vec<PathBuf>,
#[arg(long)]
pub project_directory: Option<PathBuf>,
#[arg(long)]
pub host_env_file: Option<PathBuf>,
#[arg(long)]
pub no_docker_check: bool,
#[arg(long)]
pub show_values: bool,
#[arg(long, value_enum, default_value_t = OutputFormat::Human)]
pub format: OutputFormat,
}
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum OutputFormat {
Human,
Json,
}