Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for fish and tcsh shells #1347

Merged
merged 13 commits into from
Jun 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ pub struct InitCustomization {
pub hook_on_activate: Option<String>,
pub profile_common: Option<String>,
pub profile_bash: Option<String>,
pub profile_fish: Option<String>,
pub profile_tcsh: Option<String>,
pub profile_zsh: Option<String>,
pub packages: Option<Vec<PackageToInstall>>,
}
Expand Down
48 changes: 36 additions & 12 deletions cli/flox-rust-sdk/src/models/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,16 @@ impl RawManifest {
# Scripts defined in the `[profile]` section are *sourced* by *your shell* and
# inherit environment variables set in the `[vars]` section and by `[hook]` scripts.
# The `profile.common` script is sourced by all shells and special care should be
# taken to ensure compatibility with all shells. The `profile.bash` and `profile.zsh`
# scripts are then sourced by the corresponding shell.
# taken to ensure compatibility with all shells, after which exactly one of
# `profile.{bash,fish,tcsh,zsh}` is sourced by the corresponding shell.
"#});

match customization {
InitCustomization {
profile_common: None,
profile_bash: None,
profile_fish: None,
limeytexan marked this conversation as resolved.
Show resolved Hide resolved
profile_tcsh: None,
profile_zsh: None,
..
} => {
Expand All @@ -170,6 +172,18 @@ impl RawManifest {
toml_edit::value(indent::indent_all_by(2, profile_bash)),
);
}
if let Some(profile_fish) = &customization.profile_fish {
profile_table.insert(
"fish",
toml_edit::value(indent::indent_all_by(2, profile_fish)),
);
}
if let Some(profile_tcsh) = &customization.profile_tcsh {
profile_table.insert(
"tcsh",
toml_edit::value(indent::indent_all_by(2, profile_tcsh)),
);
}
if let Some(profile_zsh) = &customization.profile_zsh {
profile_table.insert(
"zsh",
Expand Down Expand Up @@ -863,6 +877,8 @@ pub(super) mod test {
hook_on_activate: None,
profile_common: None,
profile_bash: None,
profile_fish: None,
profile_tcsh: None,
profile_zsh: None,
packages: None,
};
Expand Down Expand Up @@ -905,8 +921,8 @@ pub(super) mod test {
# Scripts defined in the `[profile]` section are *sourced* by *your shell* and
# inherit environment variables set in the `[vars]` section and by `[hook]` scripts.
# The `profile.common` script is sourced by all shells and special care should be
# taken to ensure compatibility with all shells. The `profile.bash` and `profile.zsh`
# scripts are then sourced by the corresponding shell.
# taken to ensure compatibility with all shells, after which exactly one of
# `profile.{bash,fish,tcsh,zsh}` is sourced by the corresponding shell.
[profile]
# common = """
# echo "it's gettin' flox in here"
Expand All @@ -929,6 +945,8 @@ pub(super) mod test {
hook_on_activate: None,
profile_common: None,
profile_bash: None,
profile_fish: None,
profile_tcsh: None,
profile_zsh: None,
packages: Some(vec![]),
};
Expand Down Expand Up @@ -972,8 +990,8 @@ pub(super) mod test {
# Scripts defined in the `[profile]` section are *sourced* by *your shell* and
# inherit environment variables set in the `[vars]` section and by `[hook]` scripts.
# The `profile.common` script is sourced by all shells and special care should be
# taken to ensure compatibility with all shells. The `profile.bash` and `profile.zsh`
# scripts are then sourced by the corresponding shell.
# taken to ensure compatibility with all shells, after which exactly one of
# `profile.{bash,fish,tcsh,zsh}` is sourced by the corresponding shell.
[profile]
# common = """
# echo "it's gettin' flox in here"
Expand All @@ -996,6 +1014,8 @@ pub(super) mod test {
hook_on_activate: None,
profile_common: None,
profile_bash: None,
profile_fish: None,
profile_tcsh: None,
profile_zsh: None,
packages: Some(vec![PackageToInstall {
id: "python3".to_string(),
Expand Down Expand Up @@ -1042,8 +1062,8 @@ pub(super) mod test {
# Scripts defined in the `[profile]` section are *sourced* by *your shell* and
# inherit environment variables set in the `[vars]` section and by `[hook]` scripts.
# The `profile.common` script is sourced by all shells and special care should be
# taken to ensure compatibility with all shells. The `profile.bash` and `profile.zsh`
# scripts are then sourced by the corresponding shell.
# taken to ensure compatibility with all shells, after which exactly one of
# `profile.{bash,fish,tcsh,zsh}` is sourced by the corresponding shell.
[profile]
# common = """
# echo "it's gettin' flox in here"
Expand Down Expand Up @@ -1075,6 +1095,8 @@ pub(super) mod test {
),
profile_common: None,
profile_bash: None,
profile_fish: None,
profile_tcsh: None,
profile_zsh: None,
packages: None,
};
Expand Down Expand Up @@ -1116,8 +1138,8 @@ pub(super) mod test {
# Scripts defined in the `[profile]` section are *sourced* by *your shell* and
# inherit environment variables set in the `[vars]` section and by `[hook]` scripts.
# The `profile.common` script is sourced by all shells and special care should be
# taken to ensure compatibility with all shells. The `profile.bash` and `profile.zsh`
# scripts are then sourced by the corresponding shell.
# taken to ensure compatibility with all shells, after which exactly one of
# `profile.{bash,fish,tcsh,zsh}` is sourced by the corresponding shell.
[profile]
# common = """
# echo "it's gettin' flox in here"
Expand Down Expand Up @@ -1145,6 +1167,8 @@ pub(super) mod test {
.to_string(),
),
profile_bash: None,
profile_fish: None,
profile_tcsh: None,
profile_zsh: None,
packages: None,
};
Expand Down Expand Up @@ -1187,8 +1211,8 @@ pub(super) mod test {
# Scripts defined in the `[profile]` section are *sourced* by *your shell* and
# inherit environment variables set in the `[vars]` section and by `[hook]` scripts.
# The `profile.common` script is sourced by all shells and special care should be
# taken to ensure compatibility with all shells. The `profile.bash` and `profile.zsh`
# scripts are then sourced by the corresponding shell.
# taken to ensure compatibility with all shells, after which exactly one of
# `profile.{bash,fish,tcsh,zsh}` is sourced by the corresponding shell.
[profile]
common = """
echo \"Hello from Flox\"
Expand Down
4 changes: 2 additions & 2 deletions cli/flox/doc-catalog/manifest.toml.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,8 @@ Scripts defined in the `[profile]` section are sourced by *your shell* and
inherit environment variables set in the `[vars]` section and by the `[hook]`
scripts.
The `profile.common` script is sourced for every shell,
and special care should be taken to ensure compatibility with all shells.
The `profile.<shell>` scripts are then sourced *after* `profile.common` by the
and special care should be taken to ensure compatibility with all shells,
after which exactly one of `profile.{bash,fish,tcsh,zsh}` is sourced by the
corresponding shell.

These scripts are useful for performing shell-specific customizations such as
Expand Down
2 changes: 1 addition & 1 deletion cli/flox/doc/flox-activate.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ and adds `bin` directories to your `$PATH`.
Flox will determine the parent shell from `$FLOX_SHELL` or otherwise
automatically determine the parent shell and fall back to `$SHELL`.

`flox activate` currently only supports `bash` and `zsh` shells
`flox activate` currently supports `bash`, `fish`, `tcsh` and `zsh` shells
for any of the detection mechanisms described above.

When invoked interactively,
Expand Down
2 changes: 1 addition & 1 deletion cli/flox/doc/flox.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ making them portable across the full software lifecycle.

## Command Line Completions

Flox ships with command line completions for `bash`, `zsh` and `fish`.
Flox ships with command line completions for `bash`, `fish` and `zsh`.
These completions are installed alongside Flox.

# OPTIONS
Expand Down
9 changes: 7 additions & 2 deletions cli/flox/doc/manifest.toml.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,8 @@ Scripts defined in the `[profile]` section are sourced by *your shell* and
inherit environment variables set in the `[vars]` section and by the `[hook]`
scripts.
The `profile.common` script is sourced for every shell,
and special care should be taken to ensure compatibility with all shells.
The `profile.<shell>` scripts are then sourced *after* `profile.common` by the
and special care should be taken to ensure compatibility with all shells,
after which exactly one of `profile.{bash,fish,tcsh,zsh}` is sourced by the
corresponding shell.

These scripts are useful for performing shell-specific customizations such as
Expand All @@ -329,6 +329,11 @@ bash = """
alias foo="echo bar"
set -o vi
"""
fish = """
source $venv_dir/bin/activate.fish
alias foo="echo bar"
fish_vi_key_bindings
"""
zsh = """
source $venv_dir/bin/activate
alias foo="echo bar"
Expand Down
18 changes: 13 additions & 5 deletions cli/flox/src/commands/activate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::os::unix::process::CommandExt;
use std::path::{Path, PathBuf};
use std::process::Command;

use anyhow::{bail, Context, Result};
use anyhow::{anyhow, bail, Context, Result};
use bpaf::Bpaf;
use crossterm::tty::IsTty;
use flox_rust_sdk::flox::{Flox, DEFAULT_NAME};
Expand Down Expand Up @@ -365,6 +365,12 @@ impl Activate {
.arg("--rcfile")
.arg(activation_path.join("activate").join("bash"));
},
Shell::Fish(_) => {
limeytexan marked this conversation as resolved.
Show resolved Hide resolved
return Err(anyhow!("fish not supported with environments rendered before version 1.0.5; please update environment and try again"));
},
Shell::Tcsh(_) => {
return Err(anyhow!("tcsh not supported with environments rendered before version 1.0.5; please update environment and try again"));
},
Shell::Zsh(_) => {
// From man zsh:
// Commands are then read from $ZDOTDIR/.zshenv. If the shell is a
Expand Down Expand Up @@ -516,6 +522,8 @@ impl Activate {
.map(|(key, value)| (key, shell_escape::escape(Cow::Borrowed(value))))
.map(|(key, value)| match shell {
Shell::Bash(_) => format!("export {key}={value};",),
Shell::Fish(_) => format!("set -gx {key} {value};",),
Shell::Tcsh(_) => format!("setenv {key} {value};",),
Shell::Zsh(_) => format!("export {key}={value};",),
})
.join("\n");
Expand All @@ -533,15 +541,15 @@ impl Activate {
/// Quote run args so that words don't get split,
/// but don't escape all characters.
///
/// To do this we escape `"`,
/// To do this we escape '"' and '`',
/// but we don't escape anything else.
/// We want `$` for example to be expanded by the shell.
/// We want '$' for example to be expanded by the shell.
fn quote_run_args(run_args: &[String]) -> String {
run_args
.iter()
.map(|arg| {
if arg.contains(' ') || arg.contains('"') {
format!(r#""{}""#, arg.replace('"', r#"\""#))
if arg.contains(' ') || arg.contains('"') || arg.contains('`') {
format!(r#""{}""#, arg.replace('"', r#"\""#).replace('`', r#"\`"#))
} else {
arg.to_string()
}
Expand Down
2 changes: 2 additions & 0 deletions cli/flox/src/commands/init/go.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ impl InitHook for Go {
hook_on_activate: Some(GO_HOOK.to_string()),
profile_common: None,
profile_bash: None,
profile_fish: None,
profile_tcsh: None,
profile_zsh: None,
packages: Some(vec![PackageToInstall {
id: "go".to_string(),
Expand Down
68 changes: 68 additions & 0 deletions cli/flox/src/commands/init/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ impl Init {
let mut custom_hook_on_activate_scripts: Vec<String> = vec![];
let mut custom_profile_common_scripts: Vec<String> = vec![];
let mut custom_profile_bash_scripts: Vec<String> = vec![];
let mut custom_profile_fish_scripts: Vec<String> = vec![];
let mut custom_profile_tcsh_scripts: Vec<String> = vec![];
let mut custom_profile_zsh_scripts: Vec<String> = vec![];
// Deduplicate packages with a set
let mut packages_set = HashSet::<PackageToInstall>::new();
Expand All @@ -261,6 +263,12 @@ impl Init {
if let Some(profile_bash_script) = customization.profile_bash {
custom_profile_bash_scripts.push(profile_bash_script)
}
if let Some(profile_fish_script) = customization.profile_fish {
custom_profile_fish_scripts.push(profile_fish_script)
}
if let Some(profile_tcsh_script) = customization.profile_tcsh {
custom_profile_tcsh_scripts.push(profile_tcsh_script)
}
if let Some(profile_zsh_script) = customization.profile_zsh {
custom_profile_zsh_scripts.push(profile_zsh_script)
}
Expand Down Expand Up @@ -294,6 +302,22 @@ impl Init {
# End autogenerated by Flox
", custom_profile_bash_scripts.join("\n\n")}
});
let custom_profile_fish = (!custom_profile_fish_scripts.is_empty()).then(|| {
formatdoc! {"
# Autogenerated by Flox

{}

# End autogenerated by Flox", custom_profile_fish_scripts.join("\n\n")}
});
let custom_profile_tcsh = (!custom_profile_tcsh_scripts.is_empty()).then(|| {
formatdoc! {"
# Autogenerated by Flox

{}

# End autogenerated by Flox", custom_profile_tcsh_scripts.join("\n\n")}
});
let custom_profile_zsh = (!custom_profile_zsh_scripts.is_empty()).then(|| {
formatdoc! {"
# Autogenerated by Flox
Expand All @@ -311,6 +335,8 @@ impl Init {
hook_on_activate: custom_hook_on_activate,
profile_common: custom_profile_common,
profile_bash: custom_profile_bash,
profile_fish: custom_profile_fish,
profile_tcsh: custom_profile_tcsh,
profile_zsh: custom_profile_zsh,
packages,
}
Expand Down Expand Up @@ -379,6 +405,22 @@ fn format_customization(customization: &InitCustomization) -> Result<String> {
"#, indent::indent_all_by(2, profile_bash_script)}))),
);
};
if let Some(profile_fish_script) = &customization.profile_fish {
profile_table.insert(
"fish",
Item::Value(Value::String(Formatted::new(formatdoc! {r#"
{}
"#, indent::indent_all_by(2, profile_fish_script)}))),
);
};
if let Some(profile_tcsh_script) = &customization.profile_tcsh {
profile_table.insert(
"tcsh",
Item::Value(Value::String(Formatted::new(formatdoc! {r#"
{}
"#, indent::indent_all_by(2, profile_tcsh_script)}))),
);
};
if let Some(profile_zsh_script) = &customization.profile_zsh {
profile_table.insert(
"zsh",
Expand Down Expand Up @@ -830,6 +872,8 @@ mod tests {
hook_on_activate: Some("hook_on_activate1".to_string()),
profile_common: Some("profile_common1".to_string()),
profile_bash: Some("profile_bash1".to_string()),
profile_fish: Some("profile_fish1".to_string()),
profile_tcsh: Some("profile_tcsh1".to_string()),
profile_zsh: Some("profile_zsh1".to_string()),
packages: Some(vec![
PackageToInstall {
Expand All @@ -850,6 +894,8 @@ mod tests {
hook_on_activate: Some("hook_on_activate2".to_string()),
profile_common: Some("profile_common2".to_string()),
profile_bash: Some("profile_bash2".to_string()),
profile_fish: Some("profile_fish2".to_string()),
profile_tcsh: Some("profile_tcsh2".to_string()),
profile_zsh: Some("profile_zsh2".to_string()),
packages: Some(vec![
PackageToInstall {
Expand Down Expand Up @@ -908,6 +954,28 @@ mod tests {
"#}
.to_string()
),
profile_fish: Some(
indoc! {r#"
# Autogenerated by Flox

profile_fish1

profile_fish2

# End autogenerated by Flox"#}
.to_string()
),
profile_tcsh: Some(
indoc! {r#"
# Autogenerated by Flox

profile_tcsh1

profile_tcsh2

# End autogenerated by Flox"#}
.to_string()
),
profile_zsh: Some(
indoc! {r#"
# Autogenerated by Flox
Expand Down