Skip to main content

cli/commands/
env.rs

1use clap::{Args, Subcommand};
2use std::{ffi::OsString, path::PathBuf};
3
4#[derive(Subcommand, Debug)]
5pub enum EnvCommands {
6    /// List all env variables
7    List {
8        /// Show sensitive values instead of redacting them
9        #[arg(long)]
10        reveal: bool,
11    },
12    /// Set a variable in config.toml [env]
13    Set {
14        /// Variable name (e.g. HTTP_PROXY_PORT)
15        key: String,
16        /// Variable value
17        value: String,
18        /// Write directly into the env override file that currently shadows this
19        /// key (global/overlay/project shine.env.toml) instead of refusing
20        #[arg(long)]
21        force: bool,
22    },
23    /// Delete a variable from config.toml [env]
24    Delete {
25        /// Variable name
26        key: String,
27        /// Delete directly from the env override file that currently shadows
28        /// this key (global/overlay/project shine.env.toml) instead of refusing
29        #[arg(long)]
30        force: bool,
31    },
32    /// Get a single variable value
33    Get {
34        /// Variable name
35        key: String,
36    },
37    /// Run a command with the workspace environment
38    Run(EnvRunCommand),
39    /// Transparently proxy selected commands with explicitly injected values
40    Proxy(EnvProxyCommand),
41    /// Encrypt, decrypt, export, and manage secret identities
42    Secret(EnvSecretCommand),
43}
44
45#[derive(Args, Debug)]
46pub struct EnvProxyCommand {
47    #[command(subcommand)]
48    pub command: EnvProxySubcommand,
49}
50
51#[derive(Subcommand, Debug)]
52pub enum EnvProxySubcommand {
53    /// Install a PATH shim and configure its allowed environment values
54    Install {
55        #[arg(value_name = "COMMAND")]
56        command: String,
57        #[arg(long = "with", value_name = "KEY[=ALIAS]", required = true)]
58        with: Vec<String>,
59        /// Store the rule in the current project's shine.config.toml
60        #[arg(long)]
61        project: bool,
62    },
63    /// List installed transparent command proxies
64    List,
65    /// Remove a shine-managed command proxy and its user-level rule
66    Uninstall {
67        #[arg(value_name = "COMMAND")]
68        command: String,
69    },
70    /// Enable secret injection for an installed command proxy
71    Enable {
72        #[arg(value_name = "COMMAND")]
73        command: String,
74        /// Change the rule in the current project's shine.config.toml
75        #[arg(long)]
76        project: bool,
77    },
78    /// Bypass secret injection while retaining the installed command proxy
79    Disable {
80        #[arg(value_name = "COMMAND")]
81        command: String,
82        /// Change the rule in the current project's shine.config.toml
83        #[arg(long)]
84        project: bool,
85    },
86    #[command(hide = true)]
87    Exec {
88        #[arg(long)]
89        target: PathBuf,
90        #[arg(value_name = "COMMAND")]
91        command: String,
92        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
93        args: Vec<OsString>,
94    },
95}
96
97#[derive(Args, Debug)]
98pub struct EnvSecretCommand {
99    #[command(subcommand)]
100    pub command: EnvSecretSubcommand,
101}
102
103#[derive(Subcommand, Debug)]
104pub enum EnvSecretSubcommand {
105    /// Decode and decrypt an encrypted secret from [env] (GPG or age)
106    Decrypt {
107        /// Variable name containing encrypted ciphertext
108        key: String,
109    },
110    /// Decrypt KEY_SECRET and print shell code that exports KEY
111    Export {
112        /// Variable name to export from KEY_SECRET
113        key: String,
114        /// Export under a different name in the current shell
115        #[arg(long = "as", value_name = "ALIAS")]
116        alias: Option<String>,
117    },
118    /// Encrypt stdin and print ciphertext (GPG by default, or age with --backend age)
119    Encrypt(EnvEncryptCommand),
120    /// Seal pending secrets in workspace environment files
121    Seal(EnvSealCommand),
122    /// Manage age identities used to decrypt age-backed secrets
123    Identity(EnvIdentityCommand),
124}
125
126#[derive(Args, Debug)]
127pub struct EnvEncryptCommand {
128    /// Secret backend to use: "gpg" (default) or "age"
129    #[arg(long)]
130    pub backend: Option<String>,
131    /// Recipient (repeatable): GPG key ID/fingerprint/email, or age recipient
132    #[arg(short = 'r', long = "recipient")]
133    pub recipients: Vec<String>,
134    /// Store the encrypted ciphertext in config.toml [env] instead of printing it
135    #[arg(long)]
136    pub set: Option<String>,
137    /// Read plaintext from an existing config.toml [env] variable instead of stdin
138    #[arg(long)]
139    pub from: Option<String>,
140    /// Write directly into the env override file that currently shadows the
141    /// target key (global/overlay/project shine.env.toml) instead of refusing
142    #[arg(long)]
143    pub force: bool,
144}
145
146#[derive(Args, Debug)]
147pub struct EnvSealCommand {
148    /// Seal only this environment source file
149    #[arg(value_name = "FILE")]
150    pub file: Option<PathBuf>,
151    /// Workspace definition (defaults to the nearest shine.workspace.toml)
152    #[arg(long, value_name = "FILE")]
153    pub workspace: Option<PathBuf>,
154    /// Secret backend to use: "gpg" (default) or "age"
155    #[arg(long)]
156    pub backend: Option<String>,
157    /// Recipient (repeatable): GPG key ID/fingerprint/email, or age recipient
158    #[arg(short = 'r', long = "recipient")]
159    pub recipients: Vec<String>,
160}
161
162#[derive(Args, Debug)]
163pub struct EnvIdentityCommand {
164    #[command(subcommand)]
165    pub command: EnvIdentitySubcommand,
166}
167
168#[derive(Subcommand, Debug)]
169pub enum EnvIdentitySubcommand {
170    /// Generate a new age identity, optionally backed by Touch ID (Secure Enclave)
171    Init {
172        /// Generate a Secure Enclave identity requiring Touch ID (macOS only)
173        #[arg(long)]
174        touch_id: bool,
175        /// Secure Enclave access control policy (only with --touch-id): any-biometry
176        /// (default), any-biometry-or-passcode, current-biometry, or passcode
177        #[arg(long, value_name = "POLICY")]
178        access_control: Option<String>,
179        /// Output path (defaults to <shine_dir>/age/identity.txt)
180        #[arg(short = 'o', long, value_name = "PATH")]
181        output: Option<PathBuf>,
182        /// Overwrite an existing identity file
183        #[arg(long)]
184        force: bool,
185    },
186    /// Print the recipient(s) for the configured identity file(s)
187    List,
188}
189
190#[derive(Args, Debug)]
191pub struct EnvRunCommand {
192    /// Workspace definition (defaults to the nearest shine.workspace.toml)
193    #[arg(long, value_name = "FILE")]
194    pub workspace: Option<PathBuf>,
195    /// Environment mode used to expand {mode} paths
196    #[arg(long)]
197    pub mode: Option<String>,
198    /// Skip workspace discovery entirely; use only --with values and inherited env
199    #[arg(long, conflicts_with_all = ["workspace", "mode"])]
200    pub no_workspace: bool,
201    /// Inject a config [env] value as KEY or KEY=ALIAS (repeatable)
202    #[arg(long = "with", value_name = "KEY[=ALIAS]")]
203    pub with: Vec<String>,
204    /// Command and arguments to run
205    #[arg(required = true, trailing_var_arg = true, allow_hyphen_values = true)]
206    pub command: Vec<OsString>,
207}