Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,22 @@ $ envelope import dev .env

Export variables to your shell:
```console
# sh / bash / zsh
$ export $(envelope list dev)
# or, if the above fails:
$ eval "$(envelope list dev --shell sh)"

# fish
$ envelope list dev --shell fish | source

# nu
$ envelope list dev --shell nu | from nuon | load-env

# cmd
> for /f "delims=" %i in ('envelope list example --shell cmd') do @%i

# powershell
PS> envelope list dev --shell powershell | Invoke-Expression
```

Verify which environment is active:
Expand Down Expand Up @@ -165,6 +180,18 @@ SECRET_KEY=mysecretkey123
SMTP_HOST=smtp.example.com
```

Generate shell-specific output with `--shell`:
```console
$ envelope list dev --shell kv # KEY=value
$ envelope list dev --shell sh # export KEY='value'
$ envelope list dev --shell fish # set -gx KEY 'value'
$ envelope list dev --shell nu # {"KEY": "value"} (nuon record)
> envelope list dev --shell cmd # set "KEY=value"
PS> envelope list dev --shell powershell # $env:KEY = "value"
```

Supported aliases: `bash` and `zsh` for `sh`, `nushell` for `nu`, and `pwsh` for `powershell`.

Pretty print with a table format:
```console
$ envelope list dev --pretty-print
Expand Down
30 changes: 29 additions & 1 deletion man/envelope.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,35 @@ Lists all environments.
```bash
envelope list dev
```
Lists all environment variables in the 'dev' environment.
Lists all environment variables in the 'dev' environment using the default `kv` format (`KEY=value`).

```bash
envelope list dev --shell sh
```
Lists all environment variables in the 'dev' environment as `export KEY='value'` commands compatible with POSIX-compliant shells (`sh`, `bash`, `zsh`).

```bash
envelope list dev --shell fish
```
Lists all environment variables in the 'dev' environment as `set -gx KEY 'value'` commands for Fish.

```bash
envelope list dev --shell nu
```
Lists all environment variables in the 'dev' environment as a Nushell record suitable for `load-env`.

```bash
envelope list dev --shell cmd
```
Lists all environment variables in the 'dev' environment as `set "KEY=value"` commands for Windows Command Prompt.

```bash
envelope list dev --shell powershell
```
Lists all environment variables in the 'dev' environment as `$env:KEY = "value"` commands for PowerShell.

`--shell` supports: `kv`, `sh`, `fish`, `nu`, `cmd`, `powershell`.
Aliases: `bash`/`zsh` -> `sh`, `nushell` -> `nu`, `pwsh` -> `powershell`.

```bash
envelope duplicate dev dev-local
Expand Down
89 changes: 88 additions & 1 deletion src/command/client/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,34 @@ impl Sort {
}
}

/// Valid shell output formats for raw key/value listing.
#[derive(Debug, Clone, clap::ValueEnum)]
enum Shell {
Kv,
#[clap(alias = "bash")]
#[clap(alias = "zsh")]
Sh,
Fish,
#[clap(alias = "nushell")]
Nu,
Cmd,
#[clap(alias = "pwsh")]
Powershell,
}

impl Shell {
fn to_output_format(&self) -> ops::RawOutputFormat {
match self {
Self::Kv => ops::RawOutputFormat::Kv,
Self::Sh => ops::RawOutputFormat::Sh,
Self::Fish => ops::RawOutputFormat::Fish,
Self::Nu => ops::RawOutputFormat::Nu,
Self::Cmd => ops::RawOutputFormat::Cmd,
Self::Powershell => ops::RawOutputFormat::PowerShell,
}
}
}

/// List saved environments and/or their variables
#[derive(Parser)]
pub struct Cmd {
Expand All @@ -44,6 +72,10 @@ pub struct Cmd {
#[arg(long, short)]
pretty_print: bool,

/// Output variables for the specified shell/format (default: kv).
#[arg(long, requires = "env", conflicts_with = "pretty_print")]
shell: Option<Shell>,

#[arg(long, short)]
truncate: bool,

Expand All @@ -58,7 +90,20 @@ impl Cmd {
None => ops::list_envs(&mut std::io::stdout(), db).await?,
Some(env) => {
if !self.pretty_print {
ops::list_raw(&mut std::io::stdout(), db, env, self.sort.to_str()).await?;
let output_format = self
.shell
.as_ref()
.map(Shell::to_output_format)
.unwrap_or(ops::RawOutputFormat::Kv);

ops::list_raw(
&mut std::io::stdout(),
db,
env,
self.sort.to_str(),
output_format,
)
.await?;
} else {
let truncate = match self.truncate {
true => db::Truncate::Max(60),
Expand All @@ -72,3 +117,45 @@ impl Cmd {
Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn parses_pwsh_alias_for_shell_flag() {
let cmd = <Cmd as clap::Parser>::try_parse_from(["list", "dev", "--shell", "pwsh"])
.expect("--shell pwsh should parse");

assert_eq!(
cmd.shell
.as_ref()
.expect("shell should be set")
.to_output_format(),
ops::RawOutputFormat::PowerShell
);
}

#[test]
fn parses_bash_alias_for_shell_flag() {
let cmd = <Cmd as clap::Parser>::try_parse_from(["list", "dev", "--shell", "bash"])
.expect("--shell bash should parse");

assert_eq!(
cmd.shell
.as_ref()
.expect("shell should be set")
.to_output_format(),
ops::RawOutputFormat::Sh
);
}

#[test]
fn shell_flag_requires_environment_argument() {
let err = <Cmd as clap::Parser>::try_parse_from(["list", "--shell", "sh"])
.err()
.expect("--shell without env should fail");

assert_eq!(err.kind(), clap::error::ErrorKind::MissingRequiredArgument);
}
}
14 changes: 13 additions & 1 deletion src/ops/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ use prettytable::{Table, row};
use crate::db::model::{Environment, EnvironmentRow};
use crate::db::{EnvelopeDb, Truncate};

mod format;

pub use format::RawOutputFormat;

use self::format::{write_nu_record, write_raw_entry};

pub async fn print_from_stdin() -> Result<()> {
let mut table = Table::new();
table.add_row(row!["VARIABLE", "VALUE"]);
Expand Down Expand Up @@ -65,15 +71,21 @@ pub async fn list_raw<W: Write>(
db: &EnvelopeDb,
env: &str,
sort: &str,
output_format: RawOutputFormat,
) -> Result<()> {
ensure!(
db.env_exists(env).await?,
"environment '{env}' does not exist"
);

let envs: Vec<EnvironmentRow> = db.list_kv_in_env_alt(env, Truncate::None, sort).await?;

if output_format == RawOutputFormat::Nu {
return write_nu_record(writer, &envs);
}

for env in envs {
writeln!(writer, "{}={}", &env.key, &env.value)?;
write_raw_entry(writer, &env.key, &env.value, output_format)?;
}

Ok(())
Expand Down
Loading