This repository has been archived by the owner on Oct 15, 2022. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3ee6ae1
commit df02150
Showing
8 changed files
with
154 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
//! Open up a project shell | ||
|
||
use crate::builder; | ||
use crate::builder::RunStatus; | ||
use crate::nix::CallOpts; | ||
use crate::ops::error::{ExitError, OpResult}; | ||
use crate::project::{roots::Roots, Project}; | ||
use crossbeam_channel as chan; | ||
use slog_scope::debug; | ||
use std::io; | ||
use std::io::Write; | ||
use std::path::{Path, PathBuf}; | ||
use std::process::Command; | ||
use std::time::{Duration, Instant}; | ||
use std::{env, fs, thread}; | ||
|
||
/// See the documentation for lorri::cli::Command::Shell for more | ||
/// details. | ||
pub fn main(project: Project) -> OpResult { | ||
let shell = env::var("SHELL").expect("lorri shell requires $SHELL to be set"); | ||
debug!("using shell path {}", shell); | ||
|
||
let tempdir = tempfile::tempdir().expect("failed to create temporary directory"); | ||
let mut bash_cmd = bash_cmd(project, tempdir.path())?; | ||
debug!("bash"; "command" => ?bash_cmd); | ||
bash_cmd | ||
.args(&["-c", &format!("exec {}", shell)]) | ||
.status() | ||
.expect("failed to execute bash"); | ||
Ok(()) | ||
} | ||
|
||
/// Instantiates a `Command` to start bash. | ||
pub fn bash_cmd(project: Project, tempdir: &Path) -> Result<Command, ExitError> { | ||
let (tx, rx) = chan::unbounded(); | ||
thread::spawn(move || { | ||
eprint!("lorri: building environment"); | ||
let mut last = Instant::now(); | ||
for msg in rx { | ||
// Set the maximum rate of the "progress bar" | ||
if last.elapsed() >= Duration::from_millis(500) { | ||
eprint!("."); | ||
io::stderr().flush().unwrap(); | ||
last = Instant::now(); | ||
} | ||
debug!("build"; "message" => ?msg); | ||
} | ||
eprintln!(". done"); | ||
}); | ||
|
||
let run_result = builder::run(tx, &project.nix_file, &project.cas) | ||
.map_err(|e| ExitError::temporary(format!("build failed: {:?}", e)))?; | ||
let build = match run_result.status { | ||
RunStatus::Complete(build) => Roots::from_project(&project) | ||
.create_roots(build) | ||
.map_err(|e| ExitError::temporary(format!("rooting the environment failed: {:?}", e))), | ||
e => Err(ExitError::temporary(format!("build failed: {:?}", e))), | ||
}?; | ||
|
||
let init_file = tempdir.join("init"); | ||
fs::write( | ||
&init_file, | ||
format!( | ||
r#" | ||
EVALUATION_ROOT="{}" | ||
{}"#, | ||
build.shell_gc_root, | ||
include_str!("direnv/envrc.bash") | ||
), | ||
) | ||
.expect("failed to write shell output"); | ||
|
||
debug!("building bash via runtime closure"; "closure" => crate::RUN_TIME_CLOSURE); | ||
let bash_path = CallOpts::expression(&format!("(import {}).path", crate::RUN_TIME_CLOSURE)) | ||
.value::<PathBuf>() | ||
.expect("failed to get runtime closure path"); | ||
|
||
let mut cmd = Command::new(bash_path.join("bash")); | ||
cmd.env( | ||
"BASH_ENV", | ||
init_file | ||
.to_str() | ||
.expect("script file path not UTF-8 clean"), | ||
); | ||
Ok(cmd) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
with import ../../../nix/bogus-nixpkgs {}; | ||
mkShell { | ||
env = { | ||
MY_ENV_VAR = "my_env_value"; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
use lorri::{cas::ContentAddressable, ops::shell, project::Project, NixFile}; | ||
use std::fs; | ||
use std::iter::FromIterator; | ||
use std::path::{Path, PathBuf}; | ||
|
||
#[test] | ||
fn loads_env() { | ||
let tempdir = tempfile::tempdir().expect("tempfile::tempdir() failed us!"); | ||
let project = project("loads_env", tempdir.path()); | ||
let output = shell::bash_cmd(project, tempdir.path()) | ||
.unwrap() | ||
.args(&["-c", "echo $MY_ENV_VAR"]) | ||
.output() | ||
.expect("failed to run shell"); | ||
|
||
assert_eq!( | ||
// The string conversion means we get a nice assertion failure message in case stdout does | ||
// not match what we expected. | ||
String::from_utf8(output.stdout).expect("stdout not UTF-8 clean"), | ||
"my_env_value\n" | ||
); | ||
} | ||
|
||
fn project(name: &str, cache_dir: &Path) -> Project { | ||
let test_root = PathBuf::from_iter(&[env!("CARGO_MANIFEST_DIR"), "tests", "shell", name]); | ||
let cas_dir = cache_dir.join("cas").to_owned(); | ||
fs::create_dir_all(&cas_dir).expect("failed to create CAS directory"); | ||
Project::new( | ||
NixFile::Shell(test_root.join("shell.nix")), | ||
&cache_dir.join("gc_roots").to_owned(), | ||
ContentAddressable::new(cas_dir).unwrap(), | ||
) | ||
.unwrap() | ||
} |