Skip to content

Commit 0c36574

Browse files
committed
add nushell support start 0.97.0+
Signed-off-by: Philippe Llerena <[email protected]>
1 parent 5399b8e commit 0c36574

File tree

10 files changed

+205
-27
lines changed

10 files changed

+205
-27
lines changed

crates/spfs/src/bootstrap.rs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,16 @@ pub fn build_interactive_shell_command(
186186
],
187187
vars: vec![shell_message],
188188
}),
189+
Shell::Nushell(nu) => Ok(Command {
190+
executable: nu.into(),
191+
args: vec![
192+
"--env-config".into(),
193+
rt.config.nu_env_file.as_os_str().to_owned(),
194+
"--config".into(),
195+
rt.config.nu_config_file.as_os_str().to_owned(),
196+
],
197+
vars: vec![shell_message],
198+
}),
189199
#[cfg(windows)]
190200
Shell::Powershell(ps1) => Ok(Command {
191201
executable: ps1.into(),
@@ -221,6 +231,26 @@ where
221231
let startup_file = match shell.kind() {
222232
ShellKind::Bash => &runtime.config.sh_startup_file,
223233
ShellKind::Tcsh => &runtime.config.csh_startup_file,
234+
ShellKind::Nushell => {
235+
let mut cmd = command.into();
236+
for arg in args.into_iter().map(Into::into) {
237+
cmd.push(" ");
238+
cmd.push(arg);
239+
}
240+
let args = vec![
241+
"--env-config".into(),
242+
runtime.config.nu_env_file.as_os_str().to_owned(),
243+
"--config".into(),
244+
runtime.config.nu_config_file.as_os_str().to_owned(),
245+
"-c".into(),
246+
cmd,
247+
];
248+
return Ok(Command {
249+
executable: shell.executable().into(),
250+
args,
251+
vars: vec![],
252+
});
253+
}
224254
ShellKind::Powershell => {
225255
let mut cmd = command.into();
226256
for arg in args.into_iter().map(Into::into) {
@@ -244,7 +274,6 @@ where
244274

245275
let mut shell_args = vec![startup_file.into(), command.into()];
246276
shell_args.extend(args.into_iter().map(Into::into));
247-
248277
Ok(Command {
249278
executable: shell.executable().into(),
250279
args: shell_args,
@@ -385,13 +414,15 @@ pub enum ShellKind {
385414
Bash,
386415
Tcsh,
387416
Powershell,
417+
Nushell,
388418
}
389419

390420
impl AsRef<str> for ShellKind {
391421
fn as_ref(&self) -> &str {
392422
match self {
393423
Self::Bash => "bash",
394424
Self::Tcsh => "tcsh",
425+
Self::Nushell => "nu",
395426
Self::Powershell => "powershell.exe",
396427
}
397428
}
@@ -404,6 +435,7 @@ pub enum Shell {
404435
Bash(PathBuf),
405436
#[cfg(unix)]
406437
Tcsh(PathBuf),
438+
Nushell(PathBuf),
407439
#[cfg(windows)]
408440
Powershell(PathBuf),
409441
}
@@ -415,6 +447,7 @@ impl Shell {
415447
Self::Bash(_) => ShellKind::Bash,
416448
#[cfg(unix)]
417449
Self::Tcsh(_) => ShellKind::Tcsh,
450+
Self::Nushell(_) => ShellKind::Nushell,
418451
#[cfg(windows)]
419452
Self::Powershell(_) => ShellKind::Powershell,
420453
}
@@ -427,6 +460,7 @@ impl Shell {
427460
Self::Bash(p) => p,
428461
#[cfg(unix)]
429462
Self::Tcsh(p) => p,
463+
Self::Nushell(p) => p,
430464
#[cfg(windows)]
431465
Self::Powershell(p) => p,
432466
}
@@ -442,6 +476,7 @@ impl Shell {
442476
Some(n) if n == ShellKind::Bash.as_ref() => Ok(Self::Bash(path.to_owned())),
443477
#[cfg(unix)]
444478
Some(n) if n == ShellKind::Tcsh.as_ref() => Ok(Self::Tcsh(path.to_owned())),
479+
Some(n) if n == ShellKind::Nushell.as_ref() => Ok(Self::Nushell(path.to_owned())),
445480
#[cfg(windows)]
446481
Some(n) if n == ShellKind::Powershell.as_ref() => Ok(Self::Powershell(path.to_owned())),
447482
Some(_) => Err(Error::new(format!("Unsupported shell: {path:?}"))),
@@ -477,7 +512,12 @@ impl Shell {
477512
}
478513
}
479514

480-
for kind in &[ShellKind::Bash, ShellKind::Tcsh, ShellKind::Powershell] {
515+
for kind in &[
516+
ShellKind::Bash,
517+
ShellKind::Tcsh,
518+
ShellKind::Powershell,
519+
ShellKind::Nushell,
520+
] {
481521
if let Some(path) = which(kind) {
482522
if let Ok(shell) = Shell::from_path(path) {
483523
return Ok(shell);

crates/spfs/src/graph/mod.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,11 @@ mod tree;
2020
use std::cell::RefCell;
2121

2222
pub use annotation::{
23-
Annotation,
24-
AnnotationValue,
25-
DEFAULT_SPFS_ANNOTATION_LAYER_MAX_STRING_VALUE_SIZE,
23+
Annotation, AnnotationValue, DEFAULT_SPFS_ANNOTATION_LAYER_MAX_STRING_VALUE_SIZE,
2624
};
2725
pub use blob::Blob;
2826
pub use database::{
29-
Database,
30-
DatabaseIterator,
31-
DatabaseView,
32-
DatabaseWalker,
33-
DigestSearchCriteria,
27+
Database, DatabaseIterator, DatabaseView, DatabaseWalker, DigestSearchCriteria,
3428
};
3529
pub use entry::Entry;
3630
pub use kind::{HasKind, Kind, ObjectKind};

crates/spfs/src/runtime/config.nu

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
$env.config = {
2+
show_banner: false,
3+
}
4+
print $env.SPFS_SHELL_MESSAGE?
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright (c) Contributors to the SPK project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
// https://github.com/spkenv/spk
4+
// Warning Nuhshell version >=0.96
5+
6+
use std::fs;
7+
8+
pub fn source<T>(_tmpdir: Option<&T>) -> String
9+
where
10+
T: AsRef<str>,
11+
{
12+
fs::read_to_string("/home/philippe.llerena/workspace/github.com/doubleailes/spk/crates/spfs/src/runtime/config.nu").unwrap()
13+
}

crates/spfs/src/runtime/env.nu

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
def create_left_prompt [] {
2+
let dir = match (do --ignore-shell-errors { $env.PWD | path relative-to $nu.home-path }) {
3+
null => $env.PWD
4+
'' => '~'
5+
$relative_pwd => ([~ $relative_pwd] | path join)
6+
}
7+
8+
let path_color = (if (is-admin) { ansi red_bold } else { ansi green_bold })
9+
let separator_color = (if (is-admin) { ansi light_red_bold } else { ansi light_green_bold })
10+
let path_segment = $"($path_color)($dir)(ansi reset)"
11+
12+
$path_segment | str replace --all (char path_sep) $"($separator_color)(char path_sep)($path_color)"
13+
}
14+
15+
def create_right_prompt [] {
16+
# create a right prompt in magenta with green separators and am/pm underlined
17+
let time_segment = ([
18+
(ansi reset)
19+
(ansi magenta)
20+
(date now | format date '%x %X') # try to respect user's locale
21+
] | str join | str replace --regex --all "([/:])" $"(ansi green)${1}(ansi magenta)" |
22+
str replace --regex --all "([AP]M)" $"(ansi magenta_underline)${1}")
23+
24+
let last_exit_code = if ($env.LAST_EXIT_CODE != 0) {([
25+
(ansi rb)
26+
($env.LAST_EXIT_CODE)
27+
] | str join)
28+
} else { "" }
29+
30+
([$last_exit_code, (char space), $time_segment] | str join)
31+
}
32+
33+
# Use nushell functions to define your right and left prompt
34+
$env.PROMPT_COMMAND = {|| create_left_prompt }
35+
# FIXME: This default is not implemented in rust code as of 2023-09-08.
36+
$env.PROMPT_COMMAND_RIGHT = {|| create_right_prompt }
37+
38+
# The prompt indicators are environmental variables that represent
39+
# the state of the prompt
40+
$env.PROMPT_INDICATOR = {|| "> " }
41+
$env.PROMPT_INDICATOR_VI_INSERT = {|| ": " }
42+
$env.PROMPT_INDICATOR_VI_NORMAL = {|| "> " }
43+
$env.PROMPT_MULTILINE_INDICATOR = {|| "::: " }
44+
45+
46+
$env.ENV_CONVERSIONS = {
47+
"PATH": {
48+
from_string: { |s| $s | split row (char esep) | path expand --no-symlink }
49+
to_string: { |v| $v | path expand --no-symlink | str join (char esep) }
50+
}
51+
"Path": {
52+
from_string: { |s| $s | split row (char esep) | path expand --no-symlink }
53+
to_string: { |v| $v | path expand --no-symlink | str join (char esep) }
54+
}
55+
}
56+
57+
let $spfs_startup_dir = if $nu.os-info.name == "windows" {
58+
"C:/spfs/etc/spfs/startup.d"
59+
} else if $nu.os-info.name == "linux" {
60+
"/spfs/etc/spfs/startup.d"
61+
} else {
62+
exit 1
63+
}
64+
65+
$env.NU_VENDOR_AUTOLOAD_DIR = ($spfs_startup_dir)

crates/spfs/src/runtime/env_nu.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright (c) Contributors to the SPK project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
// https://github.com/spkenv/spk
4+
// Warning Nuhshell version >=0.96
5+
6+
use std::fs;
7+
8+
pub fn source<T>(_tmpdir: Option<&T>) -> String
9+
where
10+
T: AsRef<str>,
11+
{
12+
fs::read_to_string("/home/philippe.llerena/workspace/github.com/doubleailes/spk/crates/spfs/src/runtime/env.nu").unwrap()
13+
}

crates/spfs/src/runtime/mod.rs

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
//! Handles the setup and initialization of runtime environments
66
7+
mod config_nu;
8+
mod env_nu;
79
#[cfg(unix)]
810
pub mod overlayfs;
911
#[cfg(unix)]
@@ -19,21 +21,8 @@ pub mod winfsp;
1921
#[cfg(unix)]
2022
pub use overlayfs::is_removed_entry;
2123
pub use storage::{
22-
makedirs_with_perms,
23-
Author,
24-
BindMount,
25-
Config,
26-
Data,
27-
KeyValuePair,
28-
KeyValuePairBuf,
29-
LiveLayer,
30-
LiveLayerFile,
31-
MountBackend,
32-
OwnedRuntime,
33-
Runtime,
34-
Status,
35-
Storage,
36-
STARTUP_FILES_LOCATION,
24+
makedirs_with_perms, Author, BindMount, Config, Data, KeyValuePair, KeyValuePairBuf, LiveLayer,
25+
LiveLayerFile, MountBackend, OwnedRuntime, Runtime, Status, Storage, STARTUP_FILES_LOCATION,
3726
};
3827
#[cfg(windows)]
3928
pub use winfsp::is_removed_entry;

crates/spfs/src/runtime/storage.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use tokio::io::AsyncReadExt;
2727
#[cfg(windows)]
2828
use super::startup_ps;
2929
#[cfg(unix)]
30-
use super::{startup_csh, startup_sh};
30+
use super::{config_nu, env_nu, startup_csh, startup_sh};
3131
use crate::encoding::Digest;
3232
use crate::env::SPFS_DIR_PREFIX;
3333
use crate::graph::object::Enum;
@@ -379,6 +379,9 @@ pub struct Config {
379379
pub sh_startup_file: PathBuf,
380380
/// The location of the startup script for csh-based shells
381381
pub csh_startup_file: PathBuf,
382+
/// The location of the startup script for nushell-based shells
383+
pub nu_env_file: PathBuf,
384+
pub nu_config_file: PathBuf,
382385
/// The location of the expect utility script used for csh-based shell environments
383386
/// \[DEPRECATED\] This field still exists for spk/spfs interop but is unused
384387
#[serde(skip_deserializing, default = "Config::default_csh_expect_file")]
@@ -420,6 +423,8 @@ impl Config {
420423
const SH_STARTUP_FILE: &'static str = "startup.sh";
421424
const CSH_STARTUP_FILE: &'static str = ".cshrc";
422425
const PS_STARTUP_FILE: &'static str = "startup.ps1";
426+
const NU_ENV_FILE: &'static str = "env.nu";
427+
const NU_CONFIG_FILE: &'static str = "config.nu";
423428
const DEV_NULL: &'static str = "/dev/null";
424429

425430
/// Return a dummy value for the legacy csh_expect_file field.
@@ -440,6 +445,8 @@ impl Config {
440445
csh_startup_file: root.join(Self::CSH_STARTUP_FILE),
441446
csh_expect_file: Self::default_csh_expect_file(),
442447
ps_startup_file: temp_dir().join(Self::PS_STARTUP_FILE),
448+
nu_env_file: temp_dir().join(Self::NU_ENV_FILE),
449+
nu_config_file: temp_dir().join(Self::NU_CONFIG_FILE),
443450
runtime_dir: Some(root),
444451
tmpfs_size,
445452
mount_namespace: None,
@@ -1014,6 +1021,16 @@ impl Runtime {
10141021
startup_csh::source(tmpdir_value_for_child_process),
10151022
)
10161023
.map_err(|err| Error::RuntimeWriteError(self.config.csh_startup_file.clone(), err))?;
1024+
std::fs::write(
1025+
&self.config.nu_env_file,
1026+
env_nu::source(tmpdir_value_for_child_process),
1027+
)
1028+
.map_err(|err| Error::RuntimeWriteError(self.config.nu_env_file.clone(), err))?;
1029+
std::fs::write(
1030+
&self.config.nu_config_file,
1031+
config_nu::source(tmpdir_value_for_child_process),
1032+
)
1033+
.map_err(|err| Error::RuntimeWriteError(self.config.nu_config_file.clone(), err))?;
10171034
#[cfg(windows)]
10181035
std::fs::write(
10191036
&self.config.ps_startup_file,

crates/spk-build/src/build/binary.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -688,23 +688,29 @@ where
688688

689689
let mut startup_file_csh = startup_dir.join(format!("spk_{}.csh", package.name()));
690690
let mut startup_file_sh = startup_dir.join(format!("spk_{}.sh", package.name()));
691+
let mut startup_file_nu = startup_dir.join(format!("spk_{}.nu", package.name()));
691692
let mut csh_file = std::fs::File::create(&startup_file_csh)
692693
.map_err(|err| Error::FileOpenError(startup_file_csh.to_owned(), err))?;
693694
let mut sh_file = std::fs::File::create(&startup_file_sh)
694695
.map_err(|err| Error::FileOpenError(startup_file_sh.to_owned(), err))?;
695-
696+
let mut nu_file = std::fs::File::create(&startup_file_nu)
697+
.map_err(|err| Error::FileOpenError(startup_file_nu.to_owned(), err))?;
696698
for op in ops {
697699
if let Some(priority) = op.priority() {
698700
let original_startup_file_sh_name = startup_file_sh.clone();
699701
let original_startup_file_csh_name = startup_file_csh.clone();
702+
let original_startup_file_nu_name = startup_file_nu.clone();
700703

701704
startup_file_sh.set_file_name(format!("{priority:02}_spk_{}.sh", package.name()));
702705
startup_file_csh.set_file_name(format!("{priority:02}_spk_{}.csh", package.name()));
706+
startup_file_nu.set_file_name(format!("{priority:02}_spk_{}.nu", package.name()));
703707

704708
std::fs::rename(original_startup_file_sh_name, &startup_file_sh)
705709
.map_err(|err| Error::FileWriteError(startup_file_sh.to_owned(), err))?;
706710
std::fs::rename(original_startup_file_csh_name, &startup_file_csh)
707711
.map_err(|err| Error::FileWriteError(startup_file_csh.to_owned(), err))?;
712+
std::fs::rename(original_startup_file_nu_name, &startup_file_nu)
713+
.map_err(|err| Error::FileWriteError(startup_file_nu.to_owned(), err))?;
708714

709715
continue;
710716
}
@@ -715,6 +721,9 @@ where
715721
sh_file
716722
.write_fmt(format_args!("{}\n", op.bash_source()))
717723
.map_err(|err| Error::FileWriteError(startup_file_sh.to_owned(), err))?;
724+
nu_file
725+
.write_fmt(format_args!("{}\n", op.nushell_source()))
726+
.map_err(|err| Error::FileWriteError(startup_file_nu.to_owned(), err))?;
718727
}
719728
Ok(())
720729
}

0 commit comments

Comments
 (0)