xtask-specific binaries in GRAPEVINE_XTASK_PATH

Some possible alternatives to this:

 - Keep putting them in PATH.
 - Make xtask a nix derivation. We would lose out on incremental
   compilation this way, and would end up recompiling xtask from scratch
   whenever something in the main package changed.
 - Have xtask call `nix build --inputs-from $toplevel nixpkgs#go` and
   such. Slow and tedious.
This commit is contained in:
Benjamin Lee 2024-06-21 17:08:10 -07:00
parent 102430cc79
commit f76806655f
No known key found for this signature in database
GPG key ID: FB9624E2885D55A4
4 changed files with 63 additions and 18 deletions

View file

@ -1,8 +1,10 @@
mod complement;
use std::process::ExitCode;
use std::{env, ffi::OsString, process::ExitCode};
use clap::{Parser, Subcommand};
use miette::{miette, IntoDiagnostic, Result, WrapErr};
use xshell::Shell;
#[derive(Parser)]
struct Args {
@ -16,11 +18,7 @@ enum Command {
}
fn main() -> ExitCode {
let args = Args::parse();
let result = match args.command {
Command::Complement(args) => complement::main(args),
};
let Err(e) = result else {
let Err(e) = try_main() else {
return ExitCode::SUCCESS;
};
// Include a leading newline because sometimes an error will occur in
@ -28,3 +26,49 @@ fn main() -> ExitCode {
eprintln!("\n{e:?}");
ExitCode::FAILURE
}
fn try_main() -> Result<()> {
let args = Args::parse();
let sh = new_shell()?;
match args.command {
Command::Complement(args) => complement::main(args, &sh),
}
}
fn new_shell() -> Result<Shell> {
let path = get_shell_path()?;
let sh = Shell::new()
.into_diagnostic()
.wrap_err("failed to initialize internal xshell::Shell wrapper")?;
sh.set_var("PATH", path);
Ok(sh)
}
/// Returns the value to set the `PATH` environment variable to in
/// [`xshell::Shell`] instances.
///
/// This function appends the paths from the `GRAPEVINE_XTASK_PATH` environment
/// variable to the existing value of `PATH` set in the xtask process.
///
/// Executable dependencies that are only called by commands in xtask should be
/// added to `GRAPEVINE_XTASK_PATH` instead of `PATH` in the devshell, to avoid
/// polluting the devshell path with extra entries.
fn get_shell_path() -> Result<OsString> {
let xtask_path = env::var_os("GRAPEVINE_XTASK_PATH").ok_or(miette!(
help = "This tool must be run from inside the Grapevine devshell. \
Make sure you didn't interrupt direnv or something similar.",
"GRAPEVINE_XTASK_PATH environment variable is unset"
))?;
if let Some(path) = env::var_os("PATH") {
let old_paths = env::split_paths(&path);
let xtask_paths = env::split_paths(&xtask_path);
env::join_paths(old_paths.chain(xtask_paths))
.into_diagnostic()
.wrap_err(
"error constructing new PATH value to include the paths from \
GRAPEVINE_XTASK_PATH",
)
} else {
Ok(xtask_path)
}
}