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}