Skip to content

Commit 5f73077

Browse files
committed
installer: archives without root directory can now be installed correctly
Using `tar ... --strip-components=1` with archives without root directory would result in an empty extracted directory leading to "No executable found" error. To fix this problem the `walkdir` crate was used in order to traverse the directories and find the executable (if any).
1 parent df11b23 commit 5f73077

File tree

3 files changed

+34
-21
lines changed

3 files changed

+34
-21
lines changed

src/installer/archive.rs

+20-18
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
use crate::installer::error::MapErrWithMessage;
21
use std::ffi::OsString;
3-
use std::fs::DirEntry;
42
use std::os::unix::prelude::PermissionsExt;
53
use std::path::{Path, PathBuf};
64

5+
use walkdir::WalkDir;
6+
7+
use crate::installer::error::MapErrWithMessage;
78
use crate::installer::InstallerResult;
89

910
pub struct ArchiveInstaller;
@@ -32,23 +33,24 @@ impl ArchiveInstaller {
3233
}
3334

3435
fn find_executable(directory: &Path) -> Result<ExecutableFile, String> {
35-
std::fs::read_dir(directory)
36-
.map_err_with(format!("Error reading files in {}", directory.display()))?
36+
let ignore_error = |result: walkdir::Result<walkdir::DirEntry>| result.ok();
37+
38+
WalkDir::new(directory)
39+
.max_depth(2)
40+
.into_iter()
41+
.filter_map(ignore_error)
3742
.find(Self::is_executable)
38-
.ok_or_else(|| String::from("No executable found"))?
43+
.ok_or_else(|| String::from("No executable found"))
3944
.map(ExecutableFile::from_file)
40-
.map_err_with("Cannot read file information".into())
4145
}
4246

43-
fn is_executable(entry: &std::io::Result<DirEntry>) -> bool {
44-
entry
45-
.as_ref()
46-
.map(|x| {
47-
let path = x.path();
48-
path.metadata()
49-
.map(|metadata| path.is_file() && (metadata.permissions().mode() & 0o111) != 0)
50-
.unwrap_or(false)
51-
})
47+
fn is_executable(x: &walkdir::DirEntry) -> bool {
48+
let path = x.path();
49+
let is_executable =
50+
|metadata: std::fs::Metadata| (metadata.permissions().mode() & 0o111) != 0;
51+
52+
path.metadata()
53+
.map(|metadata| path.is_file() && is_executable(metadata))
5254
.unwrap_or(false)
5355
}
5456

@@ -78,10 +80,10 @@ struct ExecutableFile {
7880
}
7981

8082
impl ExecutableFile {
81-
fn from_file(x: DirEntry) -> Self {
83+
fn from_file(x: walkdir::DirEntry) -> Self {
8284
Self {
83-
path: x.path(),
84-
name: x.file_name(),
85+
name: x.file_name().to_os_string(),
86+
path: x.path().to_path_buf(),
8587
}
8688
}
8789
}

src/installer/tar_archive.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@ impl TarArchiveInstaller {
2121
.arg("xf")
2222
.arg(source)
2323
.arg("-C")
24-
.arg(temp_dir)
25-
// Remove the root dir inside the tar archive. See https://askubuntu.com/a/470266
26-
.arg("--strip-components=1"),
24+
.arg(temp_dir),
2725
)
2826
}
2927
}

tests/integration_tests.rs

+13
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,17 @@ mod archives {
6666
let output = assert_error(result);
6767
assert_contains("No executable found", &output);
6868
}
69+
70+
#[test]
71+
fn no_root_directory() {
72+
let container = Docker::run(images::UBUNTU);
73+
74+
let result = container.exec(
75+
"dra download -s no_root_directory.tar.gz -i devmatteini/dra-tests",
76+
ExecArgs::Default,
77+
);
78+
79+
let output = assert_success(result);
80+
assert_contains("Installation completed", &output);
81+
}
6982
}

0 commit comments

Comments
 (0)