Merge branch 'master' into light_tables

This commit is contained in:
Jonathan Turner 2019-09-10 05:11:11 +12:00
commit aea11cf742
9 changed files with 472 additions and 104 deletions

View File

@ -9,21 +9,16 @@ version: 2.1
commands: commands:
check_token:
description: Check that QUAY_TOKEN is provided in environment
steps:
- run:
if [[ -z "${QUAY_TOKEN}" ]]; then
echo "QUAY_TOKEN is undefined. Add to CircleCI environment to continue."
exit 1;
fi
pull_cache: pull_cache:
description: Pulls Quay.io docker images usable for our cache description: Pulls Quay.io docker images (latest) for our cache
parameters:
tag:
type: string
default: "devel"
steps: steps:
- run: docker pull quay.io/nushell/nu:latest - run: echo "Tag is << parameters.tag >>"
- run: docker pull quay.io/nushell/nu-base:latest - run: docker pull quay.io/nushell/nu:<< parameters.tag >>
- run: docker pull quay.io/nushell/nu-base:<< parameters.tag >>
orbs: orbs:
# https://circleci.com/orbs/registry/orb/circleci/docker # https://circleci.com/orbs/registry/orb/circleci/docker
@ -40,13 +35,12 @@ workflows:
image: nushell/nu-base image: nushell/nu-base
tag: latest tag: latest
dockerfile: docker/Dockerfile.nu-base dockerfile: docker/Dockerfile.nu-base
extra_build_args: --cache-from=quay.io/nushell/nu-base:latest,quay.io/nushell/nu:latest extra_build_args: --cache-from=quay.io/nushell/nu-base:devel
filters: filters:
branches: branches:
ignore: ignore:
- master - master
before_build: before_build:
- check_token
- pull_cache - pull_cache
after_build: after_build:
- run: - run:
@ -63,21 +57,21 @@ workflows:
build_with_deploy: build_with_deploy:
jobs: jobs:
# Deploy versioned and latest images on tags (releases) only. # Deploy versioned and latest images on tags (releases) only - builds --release.
- docker/publish: - docker/publish:
image: nushell/nu-base image: nushell/nu-base
registry: quay.io registry: quay.io
tag: latest tag: latest
dockerfile: docker/Dockerfile.nu-base dockerfile: docker/Dockerfile.nu-base
extra_build_args: --cache-from=quay.io/nushell/nu-base:latest,quay.io/nushell/nu:latest extra_build_args: --cache-from=quay.io/nushell/nu-base:latest,quay.io/nushell/nu:latest --build-arg RELEASE=true
filters: filters:
branches: branches:
ignore: /.*/ ignore: /.*/
tags: tags:
only: /^v.*/ only: /^v.*/
before_build: before_build:
- check_token - run: docker pull quay.io/nushell/nu:latest
- pull_cache - run: docker pull quay.io/nushell/nu-base:latest
after_build: after_build:
- run: - run:
name: Build Multistage (smaller) container name: Build Multistage (smaller) container
@ -90,12 +84,11 @@ workflows:
echo "Version for Docker tag is ${DOCKER_TAG}" echo "Version for Docker tag is ${DOCKER_TAG}"
docker tag quay.io/nushell/nu-base:latest quay.io/nushell/nu-base:${DOCKER_TAG} docker tag quay.io/nushell/nu-base:latest quay.io/nushell/nu-base:${DOCKER_TAG}
docker tag quay.io/nushell/nu:latest quay.io/nushell/nu:${DOCKER_TAG} docker tag quay.io/nushell/nu:latest quay.io/nushell/nu:${DOCKER_TAG}
docker login -u="nushell+circleci" -p="${QUAY_TOKEN}" quay.io
docker push quay.io/nushell/nu-base docker push quay.io/nushell/nu-base
docker push quay.io/nushell/nu docker push quay.io/nushell/nu
# publish devel to Docker Hub on merge to master # publish devel to Docker Hub on merge to master (doesn't build --release)
build_with_deploy_devel: build_with_deploy_devel:
jobs: jobs:
@ -105,9 +98,8 @@ workflows:
registry: quay.io registry: quay.io
tag: devel tag: devel
dockerfile: docker/Dockerfile.nu-base dockerfile: docker/Dockerfile.nu-base
extra_build_args: --cache-from=quay.io/nushell/nu-base:latest,quay.io/nushell/nu:latest extra_build_args: --cache-from=quay.io/nushell/nu-base:devel
before_build: before_build:
- check_token
- pull_cache - pull_cache
filters: filters:
branches: branches:
@ -120,6 +112,38 @@ workflows:
- run: - run:
name: Publish Development Docker Tags name: Publish Development Docker Tags
command: | command: |
docker login -u="nushell+circleci" -p="${QUAY_TOKEN}" quay.io
docker push quay.io/nushell/nu-base:devel docker push quay.io/nushell/nu-base:devel
docker push quay.io/nushell/nu:devel docker push quay.io/nushell/nu:devel
nightly:
jobs:
- docker/publish:
image: nushell/nu-base
registry: quay.io
tag: nightly
dockerfile: docker/Dockerfile.nu-base
extra_build_args: --cache-from=quay.io/nushell/nu-base:nightly --build-arg RELEASE=true
filters:
branches:
only: master
before_build:
- run: docker pull quay.io/nushell/nu:nightly
- run: docker pull quay.io/nushell/nu-base:nightly
after_build:
- run:
name: Build Multistage (smaller) container
command: |
docker build -f docker/Dockerfile -t quay.io/nushell/nu:nightly .
- run:
name: Publish Nightly Nushell Containers
command: |
docker push quay.io/nushell/nu-base:nightly
docker push quay.io/nushell/nu:nightly
triggers:
- schedule:
cron: "0 22 * * *"
filters:
branches:
only:
- master

View File

@ -11,7 +11,7 @@ A modern shell for the GitHub era
# Status # Status
This project has reached a minimum-viable product level of quality. While contributors dogfood it as their daily driver, it may be instable for some commands. Future releases will work fill out missing features and improve stability. Its design is also subject to change as it matures. This project has reached a minimum-viable product level of quality. While contributors dogfood it as their daily driver, it may be unstable for some commands. Future releases will work fill out missing features and improve stability. Its design is also subject to change as it matures.
Nu comes with a set of built-in commands (listed below). If a command is unknown, the command will shell-out and execute it (using cmd on Windows or bash on Linux and MacOS), correctly passing through stdin, stdout and stderr, so things like your daily git workflows and even `vim` will work just fine. Nu comes with a set of built-in commands (listed below). If a command is unknown, the command will shell-out and execute it (using cmd on Windows or bash on Linux and MacOS), correctly passing through stdin, stdout and stderr, so things like your daily git workflows and even `vim` will work just fine.

View File

@ -3,3 +3,4 @@ FROM quay.io/nushell/nu-base:${FROMTAG} as base
FROM rust:1.37-slim FROM rust:1.37-slim
COPY --from=base /usr/local/bin/nu /usr/local/bin/nu COPY --from=base /usr/local/bin/nu /usr/local/bin/nu
ENTRYPOINT ["nu"] ENTRYPOINT ["nu"]
CMD ["-l", "info"]

View File

@ -1,4 +1,4 @@
FROM rust:1.37-slim FROM ubuntu:16.04
# docker build -f docker/Dockerfile.nu-base -t nushell/nu-base . # docker build -f docker/Dockerfile.nu-base -t nushell/nu-base .
# docker run -it nushell/nu-base # docker run -it nushell/nu-base
@ -7,12 +7,20 @@ ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update && apt-get install -y libssl-dev \ RUN apt-get update && apt-get install -y libssl-dev \
libxcb-composite0-dev \ libxcb-composite0-dev \
libx11-dev \ libx11-dev \
pkg-config pkg-config \
curl
RUN USER=root cargo new --bin /code
ARG RELEASE=false
WORKDIR /code WORKDIR /code
ADD . /code COPY ./rust-toolchain ./rust-toolchain
RUN cargo build --release && cargo run --release RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path --default-toolchain `cat rust-toolchain`
RUN cp target/release/nu /usr/local/bin ENV PATH=/root/.cargo/bin:$PATH
COPY . /code
RUN echo "##vso[task.prependpath]/root/.cargo/bin" && \
rustc -Vv && \
if $RELEASE; then cargo build --release && cargo run --release; \
cp target/release/nu /usr/local/bin; \
else cargo build; \
cp target/debug/nu /usr/local/bin; fi;
ENTRYPOINT ["nu"] ENTRYPOINT ["nu"]
CMD ["-l", "info"]

View File

@ -328,7 +328,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> {
let type_name = std::any::type_name::<V::Value>(); let type_name = std::any::type_name::<V::Value>();
let tagged_val_name = std::any::type_name::<Tagged<Value>>(); let tagged_val_name = std::any::type_name::<Tagged<Value>>();
if name == tagged_val_name { if type_name == tagged_val_name {
return visit::<Tagged<Value>, _>(value.val, name, fields, visitor); return visit::<Tagged<Value>, _>(value.val, name, fields, visitor);
} }
@ -467,3 +467,27 @@ impl<'a, 'de: 'a> de::SeqAccess<'de> for StructDeserializer<'a, 'de> {
return Some(self.fields.len()); return Some(self.fields.len());
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use std::any::type_name;
#[test]
fn check_type_name_properties() {
// This ensures that certain properties for the
// std::any::type_name function hold, that
// this code relies on. The type_name docs explicitly
// mention that the actual format of the output
// is unspecified and change is likely.
// This test makes sure that such change is detected
// by this test failing, and not things silently breaking.
// Specifically, we rely on this behaviour further above
// in the file to special case Tagged<Value> parsing.
let tuple = type_name::<()>();
let tagged_tuple = type_name::<Tagged<()>>();
let tagged_value = type_name::<Tagged<Value>>();
assert!(tuple != tagged_tuple);
assert!(tuple != tagged_value);
assert!(tagged_tuple != tagged_value);
}
}

View File

@ -15,7 +15,7 @@ use std::path::{Path, PathBuf};
pub struct FilesystemShell { pub struct FilesystemShell {
pub(crate) path: String, pub(crate) path: String,
last_path: String, pub(crate) last_path: String,
completer: NuCompleter, completer: NuCompleter,
hinter: HistoryHinter, hinter: HistoryHinter,
} }
@ -185,29 +185,20 @@ impl Shell for FilesystemShell {
}, },
Some(v) => { Some(v) => {
let target = v.as_path()?; let target = v.as_path()?;
if PathBuf::from("-") == target {
PathBuf::from(&self.last_path)
} else {
let path = PathBuf::from(self.path()); let path = PathBuf::from(self.path());
match dunce::canonicalize(path.join(&target).as_path()) {
match dunce::canonicalize(path.join(&target)) {
Ok(p) => p, Ok(p) => p,
Err(_) => { Err(_) => {
let error = Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Can not change to directory", "Can not change to directory",
"directory not found", "directory not found",
v.span().clone(), v.span().clone(),
)); ))
if let Some(t) = target.to_str() {
if t == "-" {
match dunce::canonicalize(PathBuf::from(self.last_path.clone()).as_path()) {
Ok(p) => p,
Err(_) => {
return error;
}
}
} else {
return error;
}
} else {
return error;
} }
} }
} }
@ -215,23 +206,12 @@ impl Shell for FilesystemShell {
}; };
let mut stream = VecDeque::new(); let mut stream = VecDeque::new();
match std::env::set_current_dir(&path) {
Ok(_) => {} stream.push_back(
Err(_) => { ReturnSuccess::change_cwd(
if let Some(directory) = args.nth(0) {
return Err(ShellError::labeled_error(
"Can not change to directory",
"directory not found",
directory.span(),
));
} else {
return Err(ShellError::string("Can not change to directory"));
}
}
}
stream.push_back(ReturnSuccess::change_cwd(
path.to_string_lossy().to_string(), path.to_string_lossy().to_string(),
)); ));
Ok(stream.into()) Ok(stream.into())
} }

View File

@ -10,16 +10,24 @@ use crate::utils::ValueStructure;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
#[derive(Clone, Debug)] #[derive(Clone)]
pub struct ValueShell { pub struct ValueShell {
pub(crate) path: String, pub(crate) path: String,
pub(crate) last_path: String,
pub(crate) value: Tagged<Value>, pub(crate) value: Tagged<Value>,
} }
impl std::fmt::Debug for ValueShell {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ValueShell @ {}", self.path)
}
}
impl ValueShell { impl ValueShell {
pub fn new(value: Tagged<Value>) -> ValueShell { pub fn new(value: Tagged<Value>) -> ValueShell {
ValueShell { ValueShell {
path: "/".to_string(), path: "/".to_string(),
last_path: "/".to_string(),
value, value,
} }
} }
@ -76,7 +84,7 @@ impl Shell for ValueShell {
} }
fn homedir(&self) -> Option<PathBuf> { fn homedir(&self) -> Option<PathBuf> {
dirs::home_dir() Some(PathBuf::from("/"))
} }
fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> { fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> {
@ -126,6 +134,8 @@ impl Shell for ValueShell {
if target == PathBuf::from("..") { if target == PathBuf::from("..") {
cwd.pop(); cwd.pop();
} else if target == PathBuf::from("-") {
cwd = PathBuf::from(&self.last_path);
} else { } else {
match target.to_str() { match target.to_str() {
Some(target) => match target.chars().nth(0) { Some(target) => match target.chars().nth(0) {
@ -200,19 +210,16 @@ impl Shell for ValueShell {
} }
fn pwd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> { fn pwd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> {
let path = PathBuf::from(&self.path());
let mut stream = VecDeque::new(); let mut stream = VecDeque::new();
stream.push_back(ReturnSuccess::value( stream.push_back(ReturnSuccess::value(Tagged::from_item(
Value::Primitive(Primitive::String(path.to_string_lossy().to_string())) Value::string(self.path()),
.simple_spanned(args.call_info.name_span), args.call_info.name_span,
)); )));
Ok(stream.into()) Ok(stream.into())
} }
fn set_path(&mut self, path: String) { fn set_path(&mut self, path: String) {
let _ = std::env::set_current_dir(&path); self.last_path = self.path.clone();
self.path = path.clone(); self.path = path.clone();
} }

View File

@ -1,9 +1,124 @@
mod helpers; mod helpers;
use helpers::Playground; use helpers::{Playground, Stub::*};
use std::path::PathBuf;
#[test] #[test]
fn cd_directory_not_found() { fn filesystem_change_from_current_directory_using_relative_path() {
Playground::setup("cd_test_1", |dirs, _| {
let actual = nu!(
cwd: dirs.root(),
r#"
cd cd_test_1
pwd | echo $it
"#
);
assert_eq!(PathBuf::from(actual), *dirs.test());
})
}
#[test]
fn filesystem_change_from_current_directory_using_absolute_path() {
Playground::setup("cd_test_2", |dirs, _| {
let actual = nu!(
cwd: dirs.test(),
r#"
cd {}
pwd | echo $it
"#,
dirs.formats()
);
assert_eq!(PathBuf::from(actual), dirs.formats());
})
}
#[test]
fn filesystem_switch_back_to_previous_working_directory() {
Playground::setup("cd_test_3", |dirs, sandbox| {
sandbox.mkdir("odin");
let actual = nu!(
cwd: dirs.test().join("odin"),
r#"
cd {}
cd -
pwd | echo $it
"#,
dirs.test()
);
assert_eq!(PathBuf::from(actual), dirs.test().join("odin"));
})
}
#[test]
fn filesytem_change_from_current_directory_using_relative_path_and_dash() {
Playground::setup("cd_test_4", |dirs, sandbox| {
sandbox.within("odin").mkdir("-");
let actual = nu!(
cwd: dirs.test(),
r#"
cd odin/-
pwd | echo $it
"#
);
assert_eq!(PathBuf::from(actual), dirs.test().join("odin").join("-"));
})
}
#[test]
fn filesystem_change_current_directory_to_parent_directory() {
Playground::setup("cd_test_5", |dirs, _| {
let actual = nu!(
cwd: dirs.test(),
r#"
cd ..
pwd | echo $it
"#
);
assert_eq!(PathBuf::from(actual), *dirs.root());
})
}
#[test]
fn filesystem_change_to_home_directory() {
Playground::setup("cd_test_6", |dirs, _| {
let actual = nu!(
cwd: dirs.test(),
r#"
cd ~
pwd | echo $it
"#
);
assert_eq!(PathBuf::from(actual), dirs::home_dir().unwrap());
})
}
#[test]
fn filesystem_change_to_a_directory_containing_spaces() {
Playground::setup("cd_test_7", |dirs, sandbox| {
sandbox.mkdir("robalino turner katz");
let actual = nu!(
cwd: dirs.test(),
r#"
cd "robalino turner katz"
pwd | echo $it
"#
);
assert_eq!(PathBuf::from(actual), dirs.test().join("robalino turner katz"));
})
}
#[test]
fn filesystem_directory_not_found() {
let actual = nu_error!( let actual = nu_error!(
cwd: "tests/fixtures", cwd: "tests/fixtures",
"cd dir_that_does_not_exist" "cd dir_that_does_not_exist"
@ -13,34 +128,237 @@ fn cd_directory_not_found() {
assert!(actual.contains("directory not found")); assert!(actual.contains("directory not found"));
} }
#[test] #[test]
fn cd_back() { fn valuesystem_change_from_current_path_using_relative_path() {
Playground::setup("cd_test_back", |dirs, sandbox| { Playground::setup("cd_test_8", |dirs, sandbox| {
sandbox sandbox
.mkdir("andres") .with_files(vec![FileWithContent(
.mkdir("odin"); "sample.toml",
r#"
[[bin]]
path = "src/plugins/turner.rs"
let odin = dirs.test().join("odin"); [[bin]]
let andres = dirs.test().join("andres"); path = "src/plugins/robalino.rs"
nu!( [[bin]]
path = "src/plugins/katz.rs"
"#
)]);
let actual = nu!(
cwd: dirs.test(), cwd: dirs.test(),
r#" r#"
cd odin enter sample.toml
mkdir a cd bin
cd ../andres pwd | echo $it
mkdir b exit
cd -
mkdir c
mkdir -
cd -
mkdir d
"# "#
); );
assert!(odin.join("a").exists()); assert_eq!(PathBuf::from(actual), PathBuf::from("/bin"));
assert!(andres.join("b").exists());
assert!(odin.join("c").exists());
assert!(odin.join("-").join("d").exists());
}) })
} }
#[test]
fn valuesystem_change_from_current_path_using_absolute_path() {
Playground::setup("cd_test_9", |dirs, sandbox| {
sandbox
.with_files(vec![FileWithContent(
"sample.toml",
r#"
[dependencies]
turner-ts = "0.1.1"
robalino-tkd = "0.0.1"
katz-ember = "0.2.3"
[[bin]]
path = "src/plugins/arepa.rs"
[[bin]]
path = "src/plugins/bbq.rs"
"#
)]);
let actual = nu!(
cwd: dirs.test(),
r#"
enter sample.toml
cd bin
cd /dependencies
pwd | echo $it
exit
"#
);
assert_eq!(PathBuf::from(actual), PathBuf::from("/dependencies"));
})
}
#[test]
fn valuesystem_switch_back_to_previous_working_path() {
Playground::setup("cd_test_10", |dirs, sandbox| {
sandbox
.with_files(vec![FileWithContent(
"sample.toml",
r#"
[dependencies]
turner-ts = "0.1.1"
robalino-tkd = "0.0.1"
katz-ember = "0.2.3"
odin-gf = "0.2.1"
[[bin]]
path = "src/plugins/arepa.rs"
[[bin]]
path = "src/plugins/bbq.rs"
"#
)]);
let actual = nu!(
cwd: dirs.test(),
r#"
enter sample.toml
cd dependencies
cd /bin
cd -
pwd | echo $it
exit
"#
);
assert_eq!(PathBuf::from(actual), PathBuf::from("/dependencies"));
})
}
#[test]
fn valuesystem_change_from_current_path_using_relative_path_and_dash() {
Playground::setup("cd_test_11", |dirs, sandbox| {
sandbox
.with_files(vec![FileWithContent(
"sample.toml",
r#"
[package]
- = ["Yehuda Katz <wycats@gmail.com>", "Jonathan Turner <jonathan.d.turner@gmail.com>", "Andrés N. Robalino <andres@androbtech.com>"]
[[bin]]
path = "src/plugins/arepa.rs"
[[bin]]
path = "src/plugins/bbq.rs"
"#
)]);
let actual = nu!(
cwd: dirs.test(),
r#"
enter sample.toml
cd package/-
cd /bin
cd -
pwd | echo $it
exit
"#
);
assert_eq!(PathBuf::from(actual), PathBuf::from("/package/-"));
})
}
#[test]
fn valuesystem_change_current_path_to_parent_path() {
Playground::setup("cd_test_12", |dirs, sandbox| {
sandbox
.with_files(vec![FileWithContent(
"sample.toml",
r#"
[package]
emberenios = ["Yehuda Katz <wycats@gmail.com>", "Jonathan Turner <jonathan.d.turner@gmail.com>", "Andrés N. Robalino <andres@androbtech.com>"]
"#
)]);
let actual = nu!(
cwd: dirs.test(),
r#"
enter sample.toml
cd package/emberenios
cd ..
pwd | echo $it
exit
"#
);
assert_eq!(PathBuf::from(actual), PathBuf::from("/package"));
})
}
#[test]
fn valuesystem_change_to_home_directory() {
Playground::setup("cd_test_13", |dirs, sandbox| {
sandbox
.with_files(vec![FileWithContent(
"sample.toml",
r#"
[paquete]
el = "pollo loco"
"#
)]);
let actual = nu!(
cwd: dirs.test(),
r#"
enter sample.toml
cd paquete
cd ~
pwd | echo $it
exit
"#
);
assert_eq!(PathBuf::from(actual), PathBuf::from("/"));
})
}
#[test]
fn valuesystem_change_to_a_path_containing_spaces() {
Playground::setup("cd_test_14", |dirs, sandbox| {
sandbox
.with_files(vec![FileWithContent(
"sample.toml",
r#"
["pa que te"]
el = "pollo loco"
"#
)]);
let actual = nu!(
cwd: dirs.test(),
r#"
enter sample.toml
cd "pa que te"
pwd | echo $it
exit
"#
);
assert_eq!(PathBuf::from(actual), PathBuf::from("/").join("pa que te"));
})
}
#[test]
fn valuesystem_path_not_found() {
let actual = nu_error!(
cwd: "tests/fixtures/formats",
r#"
enter cargo_sample.toml
cd im_a_path_that_does_not_exist
exit
"#
);
assert!(actual.contains("Can not change to path inside"));
assert!(actual.contains("No such path exists"));
}

View File

@ -227,8 +227,14 @@ impl Playground {
playground_root.join(topic).display() playground_root.join(topic).display()
)); ));
let root =
dunce::canonicalize(playground_root).expect(&format!(
"Couldn't canonicalize tests root path {}",
playground_root.display()
));
let dirs = Dirs { let dirs = Dirs {
root: PathBuf::from(playground_root), root,
test, test,
fixtures, fixtures,
}; };