eh: modernize error handling; bump dependencies
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I63e346cd38bfb6cd277f6675fcefe64e6a6a6964
This commit is contained in:
parent
e1c8ecf0b6
commit
caa6dc9951
12 changed files with 265 additions and 172 deletions
93
Cargo.lock
generated
93
Cargo.lock
generated
|
|
@ -25,9 +25,9 @@ checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.41"
|
version = "4.5.51"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9"
|
checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
|
@ -35,9 +35,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.41"
|
version = "4.5.51"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d"
|
checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"clap_lex",
|
"clap_lex",
|
||||||
|
|
@ -45,18 +45,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_complete"
|
name = "clap_complete"
|
||||||
version = "4.5.55"
|
version = "4.5.60"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a5abde44486daf70c5be8b8f8f1b66c49f86236edf6fa2abadb4d961c4c6229a"
|
checksum = "8e602857739c5a4291dfa33b5a298aeac9006185229a700e5810a3ef7272d971"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.41"
|
version = "4.5.49"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
|
checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
|
@ -72,10 +72,11 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "eh"
|
name = "eh"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"regex",
|
"regex",
|
||||||
|
"thiserror",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"yansi",
|
"yansi",
|
||||||
|
|
@ -107,12 +108,11 @@ checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-ansi-term"
|
name = "nu-ansi-term"
|
||||||
version = "0.46.0"
|
version = "0.50.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"overload",
|
"windows-sys",
|
||||||
"winapi",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -121,12 +121,6 @@ version = "1.21.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "overload"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
|
|
@ -153,9 +147,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.11.1"
|
version = "1.12.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
|
@ -165,9 +159,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.9"
|
version = "0.4.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
|
@ -206,6 +200,26 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thread_local"
|
name = "thread_local"
|
||||||
version = "1.1.9"
|
version = "1.1.9"
|
||||||
|
|
@ -260,9 +274,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-subscriber"
|
name = "tracing-subscriber"
|
||||||
version = "0.3.19"
|
version = "0.3.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
|
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"sharded-slab",
|
"sharded-slab",
|
||||||
|
|
@ -285,30 +299,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "windows-link"
|
||||||
version = "0.3.9"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi-i686-pc-windows-gnu",
|
"windows-link",
|
||||||
"winapi-x86_64-pc-windows-gnu",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-i686-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xtask"
|
name = "xtask"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
|
|
|
||||||
17
Cargo.toml
17
Cargo.toml
|
|
@ -8,16 +8,17 @@ description = "Ergonomic Nix CLI helper"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
readme = true
|
readme = true
|
||||||
rust-version = "1.85"
|
rust-version = "1.89"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
clap = { default-features = false, features = [ "std", "help", "derive" ], version = "4.5" }
|
clap = { default-features = false, features = [ "std", "help", "derive" ], version = "4.5.51" }
|
||||||
clap_complete = "4.5"
|
clap_complete = "4.5.60"
|
||||||
regex = "1.11"
|
regex = "1.12.2"
|
||||||
tracing = "0.1"
|
thiserror = "2.0.17"
|
||||||
tracing-subscriber = "0.3"
|
tracing = "0.1.41"
|
||||||
yansi = "1.0"
|
tracing-subscriber = "0.3.20"
|
||||||
|
yansi = "1.0.1"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ crate-type = ["lib"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
regex.workspace = true
|
regex.workspace = true
|
||||||
|
thiserror.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
tracing-subscriber.workspace = true
|
tracing-subscriber.workspace = true
|
||||||
yansi.workspace = true
|
yansi.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::error::Result;
|
||||||
use crate::util::{HashExtractor, NixErrorClassifier, NixFileFixer, handle_nix_with_retry};
|
use crate::util::{HashExtractor, NixErrorClassifier, NixFileFixer, handle_nix_with_retry};
|
||||||
|
|
||||||
pub fn handle_nix_build(
|
pub fn handle_nix_build(
|
||||||
|
|
@ -5,6 +6,6 @@ pub fn handle_nix_build(
|
||||||
hash_extractor: &dyn HashExtractor,
|
hash_extractor: &dyn HashExtractor,
|
||||||
fixer: &dyn NixFileFixer,
|
fixer: &dyn NixFileFixer,
|
||||||
classifier: &dyn NixErrorClassifier,
|
classifier: &dyn NixErrorClassifier,
|
||||||
) {
|
) -> Result<i32> {
|
||||||
handle_nix_with_retry("build", args, hash_extractor, fixer, classifier, false);
|
handle_nix_with_retry("build", args, hash_extractor, fixer, classifier, false)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::error::{EhError, Result};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::process::{Command, ExitStatus, Output, Stdio};
|
use std::process::{Command, ExitStatus, Output, Stdio};
|
||||||
|
|
@ -61,17 +62,17 @@ impl NixCommand {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn impure(mut self, yes: bool) -> Self {
|
#[must_use] pub const fn impure(mut self, yes: bool) -> Self {
|
||||||
self.impure = yes;
|
self.impure = yes;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interactive(mut self, yes: bool) -> Self {
|
#[must_use] pub const fn interactive(mut self, yes: bool) -> Self {
|
||||||
self.interactive = yes;
|
self.interactive = yes;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_build_logs(mut self, yes: bool) -> Self {
|
#[must_use] pub const fn print_build_logs(mut self, yes: bool) -> Self {
|
||||||
self.print_build_logs = yes;
|
self.print_build_logs = yes;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +81,7 @@ impl NixCommand {
|
||||||
pub fn run_with_logs<I: LogInterceptor + 'static>(
|
pub fn run_with_logs<I: LogInterceptor + 'static>(
|
||||||
&self,
|
&self,
|
||||||
mut interceptor: I,
|
mut interceptor: I,
|
||||||
) -> io::Result<ExitStatus> {
|
) -> Result<ExitStatus> {
|
||||||
let mut cmd = Command::new("nix");
|
let mut cmd = Command::new("nix");
|
||||||
cmd.arg(&self.subcommand);
|
cmd.arg(&self.subcommand);
|
||||||
|
|
||||||
|
|
@ -99,15 +100,21 @@ impl NixCommand {
|
||||||
cmd.stdout(Stdio::inherit());
|
cmd.stdout(Stdio::inherit());
|
||||||
cmd.stderr(Stdio::inherit());
|
cmd.stderr(Stdio::inherit());
|
||||||
cmd.stdin(Stdio::inherit());
|
cmd.stdin(Stdio::inherit());
|
||||||
return cmd.status();
|
return Ok(cmd.status()?);
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.stdout(Stdio::piped());
|
cmd.stdout(Stdio::piped());
|
||||||
cmd.stderr(Stdio::piped());
|
cmd.stderr(Stdio::piped());
|
||||||
|
|
||||||
let mut child = cmd.spawn()?;
|
let mut child = cmd.spawn()?;
|
||||||
let mut stdout = child.stdout.take().unwrap();
|
let child_stdout = child.stdout.take().ok_or_else(|| EhError::CommandFailed {
|
||||||
let mut stderr = child.stderr.take().unwrap();
|
command: format!("nix {}", self.subcommand),
|
||||||
|
})?;
|
||||||
|
let child_stderr = child.stderr.take().ok_or_else(|| EhError::CommandFailed {
|
||||||
|
command: format!("nix {}", self.subcommand),
|
||||||
|
})?;
|
||||||
|
let mut stdout = child_stdout;
|
||||||
|
let mut stderr = child_stderr;
|
||||||
|
|
||||||
let mut out_buf = [0u8; 4096];
|
let mut out_buf = [0u8; 4096];
|
||||||
let mut err_buf = [0u8; 4096];
|
let mut err_buf = [0u8; 4096];
|
||||||
|
|
@ -126,7 +133,7 @@ impl NixCommand {
|
||||||
did_something = true;
|
did_something = true;
|
||||||
}
|
}
|
||||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
|
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(EhError::Io(e)),
|
||||||
}
|
}
|
||||||
|
|
||||||
match stderr.read(&mut err_buf) {
|
match stderr.read(&mut err_buf) {
|
||||||
|
|
@ -137,7 +144,7 @@ impl NixCommand {
|
||||||
did_something = true;
|
did_something = true;
|
||||||
}
|
}
|
||||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
|
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(EhError::Io(e)),
|
||||||
}
|
}
|
||||||
|
|
||||||
if !did_something && child.try_wait()?.is_some() {
|
if !did_something && child.try_wait()?.is_some() {
|
||||||
|
|
@ -150,7 +157,7 @@ impl NixCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the command and capture all output.
|
/// Run the command and capture all output.
|
||||||
pub fn output(&self) -> io::Result<Output> {
|
pub fn output(&self) -> Result<Output> {
|
||||||
let mut cmd = Command::new("nix");
|
let mut cmd = Command::new("nix");
|
||||||
cmd.arg(&self.subcommand);
|
cmd.arg(&self.subcommand);
|
||||||
|
|
||||||
|
|
@ -174,6 +181,6 @@ impl NixCommand {
|
||||||
cmd.stderr(Stdio::piped());
|
cmd.stderr(Stdio::piped());
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.output()
|
Ok(cmd.output()?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
44
eh/src/error.rs
Normal file
44
eh/src/error.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum EhError {
|
||||||
|
#[error("Nix command failed: {0}")]
|
||||||
|
NixCommandFailed(String),
|
||||||
|
|
||||||
|
#[error("IO error: {0}")]
|
||||||
|
Io(#[from] std::io::Error),
|
||||||
|
|
||||||
|
#[error("Regex error: {0}")]
|
||||||
|
Regex(#[from] regex::Error),
|
||||||
|
|
||||||
|
#[error("UTF-8 conversion error: {0}")]
|
||||||
|
Utf8(#[from] std::string::FromUtf8Error),
|
||||||
|
|
||||||
|
#[error("Hash extraction failed")]
|
||||||
|
HashExtractionFailed,
|
||||||
|
|
||||||
|
#[error("No Nix files found")]
|
||||||
|
NoNixFilesFound,
|
||||||
|
|
||||||
|
#[error("Failed to fix hash in file: {path}")]
|
||||||
|
HashFixFailed { path: String },
|
||||||
|
|
||||||
|
#[error("Process exited with code: {code}")]
|
||||||
|
ProcessExit { code: i32 },
|
||||||
|
|
||||||
|
#[error("Command execution failed: {command}")]
|
||||||
|
CommandFailed { command: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, EhError>;
|
||||||
|
|
||||||
|
impl EhError {
|
||||||
|
#[must_use] pub const fn exit_code(&self) -> i32 {
|
||||||
|
match self {
|
||||||
|
Self::ProcessExit { code } => *code,
|
||||||
|
Self::NixCommandFailed(_) => 1,
|
||||||
|
Self::CommandFailed { .. } => 1,
|
||||||
|
_ => 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
pub mod build;
|
pub mod build;
|
||||||
pub mod command;
|
pub mod command;
|
||||||
|
pub mod error;
|
||||||
pub mod run;
|
pub mod run;
|
||||||
pub mod shell;
|
pub mod shell;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
pub use clap::{CommandFactory, Parser, Subcommand};
|
pub use clap::{CommandFactory, Parser, Subcommand};
|
||||||
|
pub use error::{EhError, Result};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(name = "eh")]
|
#[command(name = "eh")]
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
use eh::{Cli, Command, CommandFactory, Parser};
|
use eh::{Cli, Command, CommandFactory, Parser};
|
||||||
|
use error::Result;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
mod build;
|
mod build;
|
||||||
mod command;
|
mod command;
|
||||||
|
mod error;
|
||||||
mod run;
|
mod run;
|
||||||
mod shell;
|
mod shell;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
@ -17,6 +19,18 @@ fn main() {
|
||||||
.compact(); // use the `Compact` formatting style.
|
.compact(); // use the `Compact` formatting style.
|
||||||
tracing_subscriber::fmt().event_format(format).init();
|
tracing_subscriber::fmt().event_format(format).init();
|
||||||
|
|
||||||
|
let result = run_app();
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(code) => std::process::exit(code),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error: {e}");
|
||||||
|
std::process::exit(e.exit_code());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_app() -> Result<i32> {
|
||||||
let mut args = env::args();
|
let mut args = env::args();
|
||||||
let bin = args.next().unwrap_or_else(|| "eh".to_string());
|
let bin = args.next().unwrap_or_else(|| "eh".to_string());
|
||||||
let app_name = Path::new(&bin)
|
let app_name = Path::new(&bin)
|
||||||
|
|
@ -31,8 +45,7 @@ fn main() {
|
||||||
let hash_extractor = util::RegexHashExtractor;
|
let hash_extractor = util::RegexHashExtractor;
|
||||||
let fixer = util::DefaultNixFileFixer;
|
let fixer = util::DefaultNixFileFixer;
|
||||||
let classifier = util::DefaultNixErrorClassifier;
|
let classifier = util::DefaultNixErrorClassifier;
|
||||||
run::handle_nix_run(&rest, &hash_extractor, &fixer, &classifier);
|
return run::handle_nix_run(&rest, &hash_extractor, &fixer, &classifier);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"ns" => {
|
"ns" => {
|
||||||
|
|
@ -40,8 +53,7 @@ fn main() {
|
||||||
let hash_extractor = util::RegexHashExtractor;
|
let hash_extractor = util::RegexHashExtractor;
|
||||||
let fixer = util::DefaultNixFileFixer;
|
let fixer = util::DefaultNixFileFixer;
|
||||||
let classifier = util::DefaultNixErrorClassifier;
|
let classifier = util::DefaultNixErrorClassifier;
|
||||||
shell::handle_nix_shell(&rest, &hash_extractor, &fixer, &classifier);
|
return shell::handle_nix_shell(&rest, &hash_extractor, &fixer, &classifier);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"nb" => {
|
"nb" => {
|
||||||
|
|
@ -49,8 +61,7 @@ fn main() {
|
||||||
let hash_extractor = util::RegexHashExtractor;
|
let hash_extractor = util::RegexHashExtractor;
|
||||||
let fixer = util::DefaultNixFileFixer;
|
let fixer = util::DefaultNixFileFixer;
|
||||||
let classifier = util::DefaultNixErrorClassifier;
|
let classifier = util::DefaultNixErrorClassifier;
|
||||||
build::handle_nix_build(&rest, &hash_extractor, &fixer, &classifier);
|
return build::handle_nix_build(&rest, &hash_extractor, &fixer, &classifier);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
@ -63,21 +74,21 @@ fn main() {
|
||||||
|
|
||||||
match cli.command {
|
match cli.command {
|
||||||
Some(Command::Run { args }) => {
|
Some(Command::Run { args }) => {
|
||||||
run::handle_nix_run(&args, &hash_extractor, &fixer, &classifier);
|
run::handle_nix_run(&args, &hash_extractor, &fixer, &classifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Command::Shell { args }) => {
|
Some(Command::Shell { args }) => {
|
||||||
shell::handle_nix_shell(&args, &hash_extractor, &fixer, &classifier);
|
shell::handle_nix_shell(&args, &hash_extractor, &fixer, &classifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Command::Build { args }) => {
|
Some(Command::Build { args }) => {
|
||||||
build::handle_nix_build(&args, &hash_extractor, &fixer, &classifier);
|
build::handle_nix_build(&args, &hash_extractor, &fixer, &classifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
Cli::command().print_help().unwrap();
|
Cli::command().print_help()?;
|
||||||
println!();
|
println!();
|
||||||
std::process::exit(0);
|
Ok(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::error::Result;
|
||||||
use crate::util::{HashExtractor, NixErrorClassifier, NixFileFixer, handle_nix_with_retry};
|
use crate::util::{HashExtractor, NixErrorClassifier, NixFileFixer, handle_nix_with_retry};
|
||||||
|
|
||||||
pub fn handle_nix_run(
|
pub fn handle_nix_run(
|
||||||
|
|
@ -5,6 +6,6 @@ pub fn handle_nix_run(
|
||||||
hash_extractor: &dyn HashExtractor,
|
hash_extractor: &dyn HashExtractor,
|
||||||
fixer: &dyn NixFileFixer,
|
fixer: &dyn NixFileFixer,
|
||||||
classifier: &dyn NixErrorClassifier,
|
classifier: &dyn NixErrorClassifier,
|
||||||
) {
|
) -> Result<i32> {
|
||||||
handle_nix_with_retry("run", args, hash_extractor, fixer, classifier, true);
|
handle_nix_with_retry("run", args, hash_extractor, fixer, classifier, true)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::error::Result;
|
||||||
use crate::util::{HashExtractor, NixErrorClassifier, NixFileFixer, handle_nix_with_retry};
|
use crate::util::{HashExtractor, NixErrorClassifier, NixFileFixer, handle_nix_with_retry};
|
||||||
|
|
||||||
pub fn handle_nix_shell(
|
pub fn handle_nix_shell(
|
||||||
|
|
@ -5,6 +6,6 @@ pub fn handle_nix_shell(
|
||||||
hash_extractor: &dyn HashExtractor,
|
hash_extractor: &dyn HashExtractor,
|
||||||
fixer: &dyn NixFileFixer,
|
fixer: &dyn NixFileFixer,
|
||||||
classifier: &dyn NixErrorClassifier,
|
classifier: &dyn NixErrorClassifier,
|
||||||
) {
|
) -> Result<i32> {
|
||||||
handle_nix_with_retry("shell", args, hash_extractor, fixer, classifier, true);
|
handle_nix_with_retry("shell", args, hash_extractor, fixer, classifier, true)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
133
eh/src/util.rs
133
eh/src/util.rs
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::command::{NixCommand, StdIoInterceptor};
|
use crate::command::{NixCommand, StdIoInterceptor};
|
||||||
|
use crate::error::{EhError, Result};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
@ -20,61 +21,61 @@ impl HashExtractor for RegexHashExtractor {
|
||||||
r"have:\s+(sha256-[a-zA-Z0-9+/=]+)",
|
r"have:\s+(sha256-[a-zA-Z0-9+/=]+)",
|
||||||
];
|
];
|
||||||
for pattern in &patterns {
|
for pattern in &patterns {
|
||||||
if let Ok(re) = Regex::new(pattern) {
|
if let Ok(re) = Regex::new(pattern)
|
||||||
if let Some(captures) = re.captures(stderr) {
|
&& let Some(captures) = re.captures(stderr)
|
||||||
if let Some(hash) = captures.get(1) {
|
&& let Some(hash) = captures.get(1) {
|
||||||
return Some(hash.as_str().to_string());
|
return Some(hash.as_str().to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait NixFileFixer {
|
pub trait NixFileFixer {
|
||||||
fn fix_hash_in_files(&self, new_hash: &str) -> bool;
|
fn fix_hash_in_files(&self, new_hash: &str) -> Result<bool>;
|
||||||
fn find_nix_files(&self) -> Vec<PathBuf>;
|
fn find_nix_files(&self) -> Result<Vec<PathBuf>>;
|
||||||
fn fix_hash_in_file(&self, file_path: &Path, new_hash: &str) -> bool;
|
fn fix_hash_in_file(&self, file_path: &Path, new_hash: &str) -> Result<bool>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DefaultNixFileFixer;
|
pub struct DefaultNixFileFixer;
|
||||||
|
|
||||||
impl NixFileFixer for DefaultNixFileFixer {
|
impl NixFileFixer for DefaultNixFileFixer {
|
||||||
fn fix_hash_in_files(&self, new_hash: &str) -> bool {
|
fn fix_hash_in_files(&self, new_hash: &str) -> Result<bool> {
|
||||||
let nix_files = self.find_nix_files();
|
let nix_files = self.find_nix_files()?;
|
||||||
let mut fixed = false;
|
let mut fixed = false;
|
||||||
for file_path in nix_files {
|
for file_path in nix_files {
|
||||||
if self.fix_hash_in_file(&file_path, new_hash) {
|
if self.fix_hash_in_file(&file_path, new_hash)? {
|
||||||
println!("Updated hash in {}", file_path.display());
|
println!("Updated hash in {}", file_path.display());
|
||||||
fixed = true;
|
fixed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fixed
|
Ok(fixed)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_nix_files(&self) -> Vec<PathBuf> {
|
fn find_nix_files(&self) -> Result<Vec<PathBuf>> {
|
||||||
let mut files = Vec::new();
|
let mut files = Vec::new();
|
||||||
let mut stack = vec![PathBuf::from(".")];
|
let mut stack = vec![PathBuf::from(".")];
|
||||||
while let Some(dir) = stack.pop() {
|
while let Some(dir) = stack.pop() {
|
||||||
if let Ok(entries) = fs::read_dir(&dir) {
|
let entries = fs::read_dir(&dir)?;
|
||||||
for entry in entries.flatten() {
|
for entry in entries.flatten() {
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
stack.push(path);
|
stack.push(path);
|
||||||
} else if let Some(ext) = path.extension() {
|
} else if let Some(ext) = path.extension()
|
||||||
if ext.eq_ignore_ascii_case("nix") {
|
&& ext.eq_ignore_ascii_case("nix") {
|
||||||
files.push(path);
|
files.push(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if files.is_empty() {
|
||||||
|
Err(EhError::NoNixFilesFound)
|
||||||
|
} else {
|
||||||
|
Ok(files)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
files
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fix_hash_in_file(&self, file_path: &Path, new_hash: &str) -> bool {
|
fn fix_hash_in_file(&self, file_path: &Path, new_hash: &str) -> Result<bool> {
|
||||||
if let Ok(content) = fs::read_to_string(file_path) {
|
let content = fs::read_to_string(file_path)?;
|
||||||
let patterns = [
|
let patterns = [
|
||||||
(r#"hash\s*=\s*"[^"]*""#, format!(r#"hash = "{new_hash}""#)),
|
(r#"hash\s*=\s*"[^"]*""#, format!(r#"hash = "{new_hash}""#)),
|
||||||
(
|
(
|
||||||
|
|
@ -86,10 +87,10 @@ impl NixFileFixer for DefaultNixFileFixer {
|
||||||
format!(r#"outputHash = "{new_hash}""#),
|
format!(r#"outputHash = "{new_hash}""#),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
let mut new_content = content.clone();
|
let mut new_content = content;
|
||||||
let mut replaced = false;
|
let mut replaced = false;
|
||||||
for (pattern, replacement) in &patterns {
|
for (pattern, replacement) in &patterns {
|
||||||
if let Ok(re) = Regex::new(pattern) {
|
let re = Regex::new(pattern)?;
|
||||||
if re.is_match(&new_content) {
|
if re.is_match(&new_content) {
|
||||||
new_content = re
|
new_content = re
|
||||||
.replace_all(&new_content, replacement.as_str())
|
.replace_all(&new_content, replacement.as_str())
|
||||||
|
|
@ -97,12 +98,15 @@ impl NixFileFixer for DefaultNixFileFixer {
|
||||||
replaced = true;
|
replaced = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if replaced {
|
||||||
|
fs::write(file_path, new_content)
|
||||||
|
.map_err(|_| EhError::HashFixFailed {
|
||||||
|
path: file_path.to_string_lossy().to_string()
|
||||||
|
})?;
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
}
|
}
|
||||||
if replaced && fs::write(file_path, new_content).is_ok() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,24 +115,21 @@ pub trait NixErrorClassifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pre-evaluate expression to catch errors early
|
/// Pre-evaluate expression to catch errors early
|
||||||
fn pre_evaluate(_subcommand: &str, args: &[String]) -> bool {
|
fn pre_evaluate(_subcommand: &str, args: &[String]) -> Result<bool> {
|
||||||
// Find flake references or expressions to evaluate
|
// Find flake references or expressions to evaluate
|
||||||
// Only take the first non-flag argument (the package/expression)
|
// Only take the first non-flag argument (the package/expression)
|
||||||
let eval_arg = args.iter().find(|arg| !arg.starts_with('-'));
|
let eval_arg = args.iter().find(|arg| !arg.starts_with('-'));
|
||||||
|
|
||||||
let Some(eval_arg) = eval_arg else {
|
let Some(eval_arg) = eval_arg else {
|
||||||
return true; // No expression to evaluate
|
return Ok(true); // No expression to evaluate
|
||||||
};
|
};
|
||||||
|
|
||||||
let eval_cmd = NixCommand::new("eval").arg(eval_arg).arg("--raw");
|
let eval_cmd = NixCommand::new("eval").arg(eval_arg).arg("--raw");
|
||||||
|
|
||||||
let output = match eval_cmd.output() {
|
let output = eval_cmd.output()?;
|
||||||
Ok(output) => output,
|
|
||||||
Err(_) => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
return true;
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
|
@ -140,11 +141,11 @@ fn pre_evaluate(_subcommand: &str, args: &[String]) -> bool {
|
||||||
|| stderr.contains("has been marked as insecure")
|
|| stderr.contains("has been marked as insecure")
|
||||||
|| stderr.contains("has been marked as broken")
|
|| stderr.contains("has been marked as broken")
|
||||||
{
|
{
|
||||||
return true;
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For other eval failures, fail early
|
// For other eval failures, fail early
|
||||||
false
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shared retry logic for nix commands (build/run/shell).
|
/// Shared retry logic for nix commands (build/run/shell).
|
||||||
|
|
@ -155,11 +156,12 @@ pub fn handle_nix_with_retry(
|
||||||
fixer: &dyn NixFileFixer,
|
fixer: &dyn NixFileFixer,
|
||||||
classifier: &dyn NixErrorClassifier,
|
classifier: &dyn NixErrorClassifier,
|
||||||
interactive: bool,
|
interactive: bool,
|
||||||
) -> ! {
|
) -> Result<i32> {
|
||||||
// Pre-evaluate for build commands to catch errors early
|
// Pre-evaluate for build commands to catch errors early
|
||||||
if !pre_evaluate(subcommand, args) {
|
if !pre_evaluate(subcommand, args)? {
|
||||||
eprintln!("Error: Expression evaluation failed");
|
return Err(EhError::NixCommandFailed(
|
||||||
std::process::exit(1);
|
"Expression evaluation failed".to_string(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// For run commands, try interactive first to avoid breaking terminal
|
// For run commands, try interactive first to avoid breaking terminal
|
||||||
|
|
@ -170,11 +172,9 @@ pub fn handle_nix_with_retry(
|
||||||
for arg in args {
|
for arg in args {
|
||||||
cmd = cmd.arg(arg);
|
cmd = cmd.arg(arg);
|
||||||
}
|
}
|
||||||
let status = cmd
|
let status = cmd.run_with_logs(StdIoInterceptor)?;
|
||||||
.run_with_logs(StdIoInterceptor)
|
|
||||||
.expect("failed to run nix command");
|
|
||||||
if status.success() {
|
if status.success() {
|
||||||
std::process::exit(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,12 +182,13 @@ pub fn handle_nix_with_retry(
|
||||||
let output_cmd = NixCommand::new(subcommand)
|
let output_cmd = NixCommand::new(subcommand)
|
||||||
.print_build_logs(true)
|
.print_build_logs(true)
|
||||||
.args(args.iter().cloned());
|
.args(args.iter().cloned());
|
||||||
let output = output_cmd.output().expect("failed to capture output");
|
let output = output_cmd.output()?;
|
||||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
|
||||||
// Check if we need to retry with special flags
|
// Check if we need to retry with special flags
|
||||||
if let Some(new_hash) = hash_extractor.extract_hash(&stderr) {
|
if let Some(new_hash) = hash_extractor.extract_hash(&stderr) {
|
||||||
if fixer.fix_hash_in_files(&new_hash) {
|
match fixer.fix_hash_in_files(&new_hash) {
|
||||||
|
Ok(true) => {
|
||||||
info!("{}", Paint::green("✔ Fixed hash mismatch, retrying..."));
|
info!("{}", Paint::green("✔ Fixed hash mismatch, retrying..."));
|
||||||
let mut retry_cmd = NixCommand::new(subcommand)
|
let mut retry_cmd = NixCommand::new(subcommand)
|
||||||
.print_build_logs(true)
|
.print_build_logs(true)
|
||||||
|
|
@ -195,9 +196,23 @@ pub fn handle_nix_with_retry(
|
||||||
if interactive {
|
if interactive {
|
||||||
retry_cmd = retry_cmd.interactive(true);
|
retry_cmd = retry_cmd.interactive(true);
|
||||||
}
|
}
|
||||||
let retry_status = retry_cmd.run_with_logs(StdIoInterceptor).unwrap();
|
let retry_status = retry_cmd.run_with_logs(StdIoInterceptor)?;
|
||||||
std::process::exit(retry_status.code().unwrap_or(1));
|
return Ok(retry_status.code().unwrap_or(1));
|
||||||
}
|
}
|
||||||
|
Ok(false) => {
|
||||||
|
// No files were fixed, continue with normal error handling
|
||||||
|
}
|
||||||
|
Err(EhError::NoNixFilesFound) => {
|
||||||
|
warn!("No .nix files found to fix hash in");
|
||||||
|
// Continue with normal error handling
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if stderr.contains("hash") || stderr.contains("sha256") {
|
||||||
|
// If there's a hash-related error but we couldn't extract it, that's a failure
|
||||||
|
return Err(EhError::HashExtractionFailed);
|
||||||
}
|
}
|
||||||
|
|
||||||
if classifier.should_retry(&stderr) {
|
if classifier.should_retry(&stderr) {
|
||||||
|
|
@ -214,8 +229,8 @@ pub fn handle_nix_with_retry(
|
||||||
if interactive {
|
if interactive {
|
||||||
retry_cmd = retry_cmd.interactive(true);
|
retry_cmd = retry_cmd.interactive(true);
|
||||||
}
|
}
|
||||||
let retry_status = retry_cmd.run_with_logs(StdIoInterceptor).unwrap();
|
let retry_status = retry_cmd.run_with_logs(StdIoInterceptor)?;
|
||||||
std::process::exit(retry_status.code().unwrap_or(1));
|
return Ok(retry_status.code().unwrap_or(1));
|
||||||
}
|
}
|
||||||
if stderr.contains("has been marked as insecure") && stderr.contains("refusing") {
|
if stderr.contains("has been marked as insecure") && stderr.contains("refusing") {
|
||||||
warn!(
|
warn!(
|
||||||
|
|
@ -232,8 +247,8 @@ pub fn handle_nix_with_retry(
|
||||||
if interactive {
|
if interactive {
|
||||||
retry_cmd = retry_cmd.interactive(true);
|
retry_cmd = retry_cmd.interactive(true);
|
||||||
}
|
}
|
||||||
let retry_status = retry_cmd.run_with_logs(StdIoInterceptor).unwrap();
|
let retry_status = retry_cmd.run_with_logs(StdIoInterceptor)?;
|
||||||
std::process::exit(retry_status.code().unwrap_or(1));
|
return Ok(retry_status.code().unwrap_or(1));
|
||||||
}
|
}
|
||||||
if stderr.contains("has been marked as broken") && stderr.contains("refusing") {
|
if stderr.contains("has been marked as broken") && stderr.contains("refusing") {
|
||||||
warn!(
|
warn!(
|
||||||
|
|
@ -248,19 +263,21 @@ pub fn handle_nix_with_retry(
|
||||||
if interactive {
|
if interactive {
|
||||||
retry_cmd = retry_cmd.interactive(true);
|
retry_cmd = retry_cmd.interactive(true);
|
||||||
}
|
}
|
||||||
let retry_status = retry_cmd.run_with_logs(StdIoInterceptor).unwrap();
|
let retry_status = retry_cmd.run_with_logs(StdIoInterceptor)?;
|
||||||
std::process::exit(retry_status.code().unwrap_or(1));
|
return Ok(retry_status.code().unwrap_or(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the first attempt succeeded, we're done
|
// If the first attempt succeeded, we're done
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
std::process::exit(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, show the error and exit
|
// Otherwise, show the error and return error
|
||||||
std::io::stderr().write_all(output.stderr.as_ref()).unwrap();
|
std::io::stderr().write_all(&output.stderr)?;
|
||||||
std::process::exit(output.status.code().unwrap_or(1));
|
Err(EhError::ProcessExit {
|
||||||
|
code: output.status.code().unwrap_or(1),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
pub struct DefaultNixErrorClassifier;
|
pub struct DefaultNixErrorClassifier;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ enum Binary {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Binary {
|
impl Binary {
|
||||||
fn name(self) -> &'static str {
|
const fn name(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::Nr => "nr",
|
Self::Nr => "nr",
|
||||||
Self::Ns => "ns",
|
Self::Ns => "ns",
|
||||||
|
|
@ -135,14 +135,14 @@ fn create_multicall_binaries(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_completions(shell: Shell, output_dir: &Path) -> Result<(), Box<dyn error::Error>> {
|
fn generate_completions(shell: Shell, output_dir: &Path) -> Result<(), Box<dyn error::Error>> {
|
||||||
println!("generating {} completions...", shell);
|
println!("generating {shell} completions...");
|
||||||
|
|
||||||
fs::create_dir_all(output_dir)?;
|
fs::create_dir_all(output_dir)?;
|
||||||
|
|
||||||
let mut cmd = eh::Cli::command();
|
let mut cmd = eh::Cli::command();
|
||||||
let bin_name = "eh";
|
let bin_name = "eh";
|
||||||
|
|
||||||
let completion_file = output_dir.join(format!("{}.{}", bin_name, shell));
|
let completion_file = output_dir.join(format!("{bin_name}.{shell}"));
|
||||||
let mut file = fs::File::create(&completion_file)?;
|
let mut file = fs::File::create(&completion_file)?;
|
||||||
|
|
||||||
generate(shell, &mut cmd, bin_name, &mut file);
|
generate(shell, &mut cmd, bin_name, &mut file);
|
||||||
|
|
@ -152,7 +152,7 @@ fn generate_completions(shell: Shell, output_dir: &Path) -> Result<(), Box<dyn e
|
||||||
// Create symlinks for multicall binaries
|
// Create symlinks for multicall binaries
|
||||||
let multicall_names = ["nb", "nr", "ns"];
|
let multicall_names = ["nb", "nr", "ns"];
|
||||||
for name in &multicall_names {
|
for name in &multicall_names {
|
||||||
let symlink_path = output_dir.join(format!("{}.{}", name, shell));
|
let symlink_path = output_dir.join(format!("{name}.{shell}"));
|
||||||
if symlink_path.exists() {
|
if symlink_path.exists() {
|
||||||
fs::remove_file(&symlink_path)?;
|
fs::remove_file(&symlink_path)?;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue