Commit 67e909b6 authored by Wohlgemuth, Jason's avatar Wohlgemuth, Jason
Browse files

feat: Add capability to download binaries

parent 2b39737a
Loading
Loading
Loading
Loading
Loading
+394 −367
Original line number Diff line number Diff line
@@ -108,144 +108,165 @@ LH:0
end_of_record
TN:
SF:/root/dev/command/pipe-cli/src/commands/run/mod.rs
FN:19,run
FNF:1
FNDA:0,run
DA:19,0
DA:29,0
FNF:0
DA:23,0
DA:24,0
DA:30,0
DA:31,0
DA:32,0
DA:33,0
DA:34,0
DA:35,0
DA:36,0
DA:38,0
DA:39,0
DA:40,0
DA:42,0
DA:43,0
DA:45,0
DA:46,0
DA:47,0
DA:50,0
DA:51,0
DA:53,0
DA:54,0
DA:55,0
DA:56,0
DA:57,0
DA:58,0
DA:62,0
DA:60,0
DA:61,0
DA:63,0
DA:65,0
DA:66,0
DA:67,0
DA:69,0
DA:70,0
DA:72,0
DA:73,0
DA:74,0
DA:77,0
DA:78,0
DA:79,0
DA:80,0
DA:82,0
DA:84,0
DA:85,0
DA:86,0
DA:88,0
DA:91,0
DA:92,0
DA:93,0
DA:94,0
DA:95,0
DA:96,0
DA:98,0
DA:97,0
DA:99,0
DA:100,0
DA:101,0
DA:102,0
DA:103,0
DA:107,0
DA:109,0
DA:111,0
DA:113,0
DA:114,0
DA:115,0
DA:118,0
DA:119,0
DA:120,0
DA:123,0
DA:125,0
DA:126,0
DA:128,0
DA:131,0
DA:132,0
DA:134,0
DA:130,0
DA:133,0
DA:135,0
DA:137,0
DA:138,0
DA:139,0
DA:140,0
DA:141,0
DA:142,0
DA:144,0
DA:143,0
DA:146,0
DA:147,0
DA:148,0
DA:151,0
DA:153,0
DA:154,0
DA:155,0
DA:156,0
DA:157,0
DA:159,0
DA:163,0
DA:165,0
DA:167,0
DA:162,0
DA:164,0
DA:166,0
DA:168,0
DA:172,0
DA:182,0
DA:169,0
DA:173,0
DA:174,0
DA:175,0
DA:178,0
DA:180,0
DA:181,0
DA:183,0
DA:184,0
DA:185,0
DA:186,0
DA:187,0
DA:189,0
DA:190,0
DA:192,0
DA:193,0
DA:194,0
DA:195,0
DA:196,0
DA:197,0
DA:200,0
DA:203,0
DA:205,0
DA:207,0
DA:199,0
DA:209,0
DA:210,0
DA:211,0
DA:212,0
DA:213,0
DA:214,0
DA:215,0
DA:216,0
DA:217,0
DA:218,0
DA:219,0
DA:221,0
DA:224,0
DA:220,0
DA:222,0
DA:223,0
DA:227,0
DA:230,0
DA:232,0
DA:233,0
DA:234,0
DA:235,0
DA:236,0
DA:237,0
DA:238,0
DA:239,0
DA:240,0
DA:243,0
DA:246,0
DA:250,0
DA:251,0
DA:242,0
DA:247,0
DA:248,0
DA:252,0
DA:253,0
DA:255,0
DA:256,0
DA:258,0
DA:259,0
DA:260,0
DA:261,0
DA:262,0
DA:263,0
DA:264,0
DA:265,0
DA:266,0
DA:267,0
DA:268,0
DA:269,0
DA:270,0
DA:271,0
DA:272,0
DA:273,0
DA:274,0
DA:276,0
DA:278,0
DA:281,0
DA:283,0
LF:134
DA:279,0
DA:282,0
DA:285,0
DA:287,0
DA:288,0
DA:289,0
DA:290,0
DA:291,0
DA:292,0
DA:293,0
DA:295,0
DA:298,0
DA:301,0
DA:305,0
DA:306,0
DA:307,0
DA:308,0
DA:310,0
DA:311,0
DA:313,0
DA:314,0
DA:315,0
DA:316,0
DA:317,0
DA:318,0
DA:319,0
DA:320,0
DA:322,0
DA:325,0
DA:328,0
DA:331,0
DA:333,0
DA:336,0
DA:338,0
LF:157
LH:0
end_of_record
TN:
@@ -391,45 +412,46 @@ FN:641,Label::fail
FN:645,Label::found
FN:649,Label::not_found
FN:653,Label::output
FN:657,Label::read
FN:661,Label::rejected
FN:665,Label::run
FN:669,Label::using
FN:675,ModuleUri<'_>::from
FN:685,ModuleUri<'_>::hash
FN:709,ModuleUri<'_>::get_whitelist
FN:712,ModuleUri<'_>::is_allowed
FN:715,ModuleUri<'_>::test
FN:716,ModuleUri<'_>::test::check
FN:733,ModuleUri<'_>::working_directory
FN:745,SemanticVersion::from_string
FN:768,Script::expand_arguments
FN:803,Script::expand_arguments_from
FN:838,Script::from_module
FN:896,Script::from_path
FN:911,Script::from_template
FN:931,Script::get_command
FN:941,Script::get_arguments
FN:981,Script::maybe_create_virtual_environment
FN:1026,Script::maybe_git_clone
FN:1033,Script::python_default
FN:1047,Script::run
FN:1114,Script::with_container
FN:1121,Script::with_current_dir
FN:1126,Script::with_envs
FN:1168,Script::with_virtual_environment
FN:1174,ScriptTemplate::from_path
FN:1186,ScriptTemplate::resolve_python_virtual_environment
FN:1214,StringTemplate::new
FN:1223,StringTemplate::expand
FN:1230,StringTemplate::get_variable
FN:1260,StringTemplate::is_handlebars
FN:1295,StringTemplate::remove_spaces
FN:1305,add_dot_slash
FN:1313,add_forward_slash
FN:1320,download_binary
FN:1321,download_binary::download
FNF:61
FN:657,Label::pass
FN:661,Label::read
FN:665,Label::rejected
FN:669,Label::run
FN:673,Label::using
FN:679,ModuleUri<'_>::from
FN:689,ModuleUri<'_>::hash
FN:713,ModuleUri<'_>::get_whitelist
FN:716,ModuleUri<'_>::is_allowed
FN:719,ModuleUri<'_>::test
FN:720,ModuleUri<'_>::test::check
FN:737,ModuleUri<'_>::working_directory
FN:749,SemanticVersion::from_string
FN:772,Script::expand_arguments
FN:807,Script::expand_arguments_from
FN:842,Script::from_module
FN:900,Script::from_path
FN:915,Script::from_template
FN:935,Script::get_command
FN:945,Script::get_arguments
FN:985,Script::maybe_create_virtual_environment
FN:1030,Script::maybe_git_clone
FN:1037,Script::python_default
FN:1051,Script::run
FN:1118,Script::with_container
FN:1125,Script::with_current_dir
FN:1130,Script::with_envs
FN:1172,Script::with_virtual_environment
FN:1178,ScriptTemplate::from_path
FN:1190,ScriptTemplate::resolve_python_virtual_environment
FN:1218,StringTemplate::new
FN:1227,StringTemplate::expand
FN:1234,StringTemplate::get_variable
FN:1264,StringTemplate::is_handlebars
FN:1299,StringTemplate::remove_spaces
FN:1309,add_dot_slash
FN:1317,add_forward_slash
FN:1324,download_binary
FN:1325,download_binary::download
FNF:62
FNDA:1,Command::test
FNDA:1,Config::read
FNDA:1,Config::read_json
@@ -453,6 +475,7 @@ FNDA:0,Label::fail
FNDA:0,Label::found
FNDA:0,Label::not_found
FNDA:1,Label::output
FNDA:0,Label::pass
FNDA:0,Label::read
FNDA:0,Label::rejected
FNDA:0,Label::run
@@ -494,10 +517,10 @@ FNDA:0,download_binary::download
DA:446,1
DA:447,2
DA:448,1
DA:449,3
DA:450,2
DA:452,3
DA:453,1
DA:449,4
DA:450,4
DA:452,6
DA:453,2
DA:456,0
DA:457,0
DA:462,3
@@ -519,7 +542,7 @@ DA:494,2
DA:495,2
DA:497,0
DA:498,0
DA:501,2
DA:501,3
DA:502,2
DA:503,4
DA:504,0
@@ -630,292 +653,291 @@ DA:667,0
DA:669,0
DA:670,0
DA:671,0
DA:675,2
DA:676,2
DA:677,5
DA:678,2
DA:673,0
DA:674,0
DA:675,0
DA:679,2
DA:680,0
DA:682,1
DA:685,1
DA:680,2
DA:681,5
DA:682,2
DA:683,2
DA:684,0
DA:686,1
DA:687,1
DA:688,1
DA:689,2
DA:690,0
DA:692,4
DA:689,1
DA:690,1
DA:691,1
DA:692,1
DA:693,2
DA:694,0
DA:696,4
DA:697,4
DA:699,4
DA:700,2
DA:702,2
DA:705,0
DA:700,4
DA:701,4
DA:703,4
DA:704,2
DA:706,2
DA:709,0
DA:710,0
DA:712,0
DA:713,0
DA:715,0
DA:714,0
DA:716,0
DA:717,0
DA:719,0
DA:720,0
DA:721,0
DA:722,0
DA:723,0
DA:724,0
DA:725,0
DA:726,0
DA:727,0
DA:728,0
DA:729,0
DA:731,0
DA:733,0
DA:734,0
DA:735,0
DA:736,0
DA:737,0
DA:738,0
DA:739,0
DA:740,0
DA:745,1
DA:746,1
DA:747,3
DA:748,3
DA:749,3
DA:768,1
DA:769,2
DA:772,2
DA:773,1
DA:774,2
DA:775,1
DA:776,1
DA:741,0
DA:743,0
DA:744,0
DA:749,1
DA:750,1
DA:751,3
DA:752,3
DA:753,3
DA:772,1
DA:773,2
DA:776,2
DA:777,1
DA:778,2
DA:782,1
DA:783,1
DA:803,1
DA:804,2
DA:807,2
DA:808,1
DA:809,2
DA:810,2
DA:779,1
DA:780,1
DA:782,2
DA:786,1
DA:787,1
DA:807,1
DA:808,2
DA:811,2
DA:812,1
DA:813,2
DA:815,1
DA:814,2
DA:817,2
DA:821,1
DA:822,1
DA:838,0
DA:840,0
DA:841,0
DA:819,1
DA:821,2
DA:825,1
DA:826,1
DA:842,0
DA:844,0
DA:845,0
DA:846,0
DA:847,0
DA:848,0
DA:849,0
DA:850,0
DA:851,0
DA:852,0
DA:853,0
DA:855,0
DA:856,0
DA:857,0
DA:858,0
DA:859,0
DA:860,0
DA:861,0
DA:862,0
DA:863,0
DA:864,0
DA:866,0
DA:867,0
DA:869,0
DA:870,0
DA:868,0
DA:871,0
DA:873,0
DA:879,0
DA:880,0
DA:896,1
DA:897,1
DA:898,1
DA:911,1
DA:912,1
DA:913,3
DA:917,2
DA:919,2
DA:874,0
DA:877,0
DA:883,0
DA:884,0
DA:900,1
DA:901,1
DA:902,1
DA:915,1
DA:916,1
DA:917,3
DA:921,2
DA:925,1
DA:926,1
DA:927,1
DA:929,2
DA:923,2
DA:925,2
DA:929,1
DA:930,1
DA:931,1
DA:932,1
DA:933,1
DA:934,1
DA:933,2
DA:935,1
DA:936,1
DA:937,2
DA:937,1
DA:938,1
DA:941,1
DA:940,1
DA:941,2
DA:942,1
DA:943,1
DA:944,1
DA:945,1
DA:946,1
DA:947,0
DA:949,0
DA:950,0
DA:953,2
DA:954,1
DA:956,1
DA:947,1
DA:948,1
DA:949,1
DA:950,1
DA:951,0
DA:953,0
DA:954,0
DA:957,2
DA:958,1
DA:959,1
DA:960,1
DA:961,2
DA:962,2
DA:962,1
DA:963,1
DA:964,1
DA:965,2
DA:966,2
DA:967,2
DA:969,0
DA:970,0
DA:973,1
DA:974,1
DA:976,1
DA:981,0
DA:986,0
DA:987,0
DA:988,0
DA:968,1
DA:970,2
DA:971,2
DA:973,0
DA:974,0
DA:977,1
DA:978,1
DA:980,1
DA:985,0
DA:990,0
DA:991,0
DA:992,0
DA:993,0
DA:994,0
DA:995,0
DA:996,0
DA:997,0
DA:998,0
DA:999,0
DA:1000,0
DA:1001,0
DA:1002,0
DA:1003,0
DA:1004,0
DA:1005,0
DA:1006,0
DA:1007,0
DA:1009,0
DA:1010,0
DA:1013,0
DA:1014,0
DA:1017,0
DA:1022,0
DA:1026,1
DA:1027,2
DA:1028,2
DA:1029,2
DA:1030,3
DA:1031,1
DA:1033,0
DA:1034,0
DA:1035,0
DA:1043,0
DA:1047,1
DA:1048,1
DA:1049,1
DA:1050,2
DA:1051,2
DA:1052,3
DA:1018,0
DA:1021,0
DA:1026,0
DA:1030,1
DA:1031,2
DA:1032,2
DA:1033,2
DA:1034,3
DA:1035,1
DA:1037,0
DA:1038,0
DA:1039,0
DA:1047,0
DA:1051,1
DA:1052,1
DA:1053,1
DA:1054,7
DA:1054,2
DA:1055,2
DA:1056,1
DA:1056,3
DA:1057,1
DA:1058,1
DA:1059,1
DA:1064,2
DA:1065,1
DA:1067,0
DA:1068,0
DA:1071,1
DA:1072,1
DA:1073,2
DA:1074,2
DA:1058,7
DA:1059,2
DA:1060,1
DA:1061,1
DA:1062,1
DA:1063,1
DA:1068,2
DA:1069,1
DA:1071,0
DA:1072,0
DA:1075,1
DA:1076,1
DA:1077,2
DA:1078,1
DA:1080,0
DA:1081,0
DA:1084,1
DA:1085,1
DA:1086,2
DA:1087,0
DA:1088,0
DA:1091,2
DA:1092,1
DA:1093,3
DA:1094,1
DA:1095,3
DA:1096,3
DA:1097,1
DA:1099,0
DA:1100,0
DA:1101,0
DA:1078,2
DA:1081,2
DA:1082,1
DA:1084,0
DA:1085,0
DA:1088,1
DA:1089,1
DA:1090,2
DA:1091,0
DA:1092,0
DA:1095,2
DA:1096,1
DA:1097,3
DA:1098,1
DA:1099,3
DA:1100,3
DA:1101,1
DA:1103,0
DA:1104,0
DA:1105,0
DA:1106,0
DA:1108,0
DA:1109,0
DA:1110,0
DA:1111,0
DA:1114,1
DA:1115,1
DA:1116,1
DA:1121,1
DA:1122,2
DA:1123,1
DA:1126,0
DA:1127,0
DA:1128,0
DA:1168,1
DA:1169,1
DA:1170,1
DA:1114,0
DA:1115,0
DA:1118,1
DA:1119,1
DA:1120,1
DA:1125,1
DA:1126,2
DA:1127,1
DA:1130,0
DA:1131,0
DA:1132,0
DA:1172,1
DA:1173,1
DA:1174,1
DA:1175,2
DA:1176,1
DA:1178,0
DA:1179,0
DA:1182,2
DA:1184,1
DA:1186,0
DA:1187,0
DA:1188,0
DA:1189,0
DA:1178,1
DA:1179,2
DA:1180,1
DA:1182,0
DA:1183,0
DA:1186,2
DA:1188,1
DA:1190,0
DA:1191,0
DA:1192,0
DA:1193,0
DA:1194,0
DA:1195,0
DA:1196,0
DA:1197,0
DA:1202,0
DA:1203,0
DA:1204,0
DA:1214,1
DA:1216,1
DA:1223,1
DA:1224,4
DA:1225,3
DA:1230,1
DA:1231,2
DA:1232,2
DA:1233,1
DA:1234,2
DA:1236,0
DA:1260,1
DA:1261,2
DA:1262,2
DA:1263,1
DA:1264,0
DA:1266,0
DA:1295,1
DA:1296,2
DA:1297,1
DA:1298,0
DA:1300,3
DA:1199,0
DA:1200,0
DA:1201,0
DA:1206,0
DA:1207,0
DA:1208,0
DA:1218,1
DA:1220,1
DA:1227,1
DA:1228,4
DA:1229,3
DA:1234,1
DA:1235,2
DA:1236,2
DA:1237,1
DA:1238,2
DA:1240,0
DA:1264,1
DA:1265,2
DA:1266,2
DA:1267,1
DA:1268,0
DA:1270,0
DA:1299,1
DA:1300,2
DA:1301,1
DA:1302,0
DA:1304,3
DA:1305,1
DA:1306,2
DA:1307,2
DA:1309,3
DA:1313,1
DA:1314,2
DA:1315,0
DA:1317,2
DA:1320,0
DA:1321,0
DA:1322,0
DA:1323,0
DA:1309,1
DA:1310,2
DA:1311,2
DA:1313,3
DA:1317,1
DA:1318,2
DA:1319,0
DA:1321,2
DA:1324,0
DA:1325,0
DA:1326,0
@@ -924,55 +946,60 @@ DA:1328,0
DA:1329,0
DA:1330,0
DA:1331,0
DA:1332,0
DA:1333,0
DA:1334,0
DA:1335,0
DA:1338,0
DA:1337,0
DA:1339,0
DA:1340,0
DA:1352,1
DA:1353,2
DA:1354,2
DA:1355,2
DA:1356,2
DA:1357,5
DA:1360,1
DA:1362,2
DA:1364,4
DA:1367,0
DA:1394,1
DA:1395,2
DA:1396,2
DA:1397,1
DA:1398,6
DA:1410,1
DA:1412,0
DA:1414,1
DA:1429,1
DA:1430,1
DA:1433,1
DA:1434,3
DA:1446,1
DA:1447,1
DA:1450,0
DA:1451,0
DA:1452,0
DA:1454,0
DA:1458,0
DA:1342,0
DA:1343,0
DA:1344,0
DA:1345,0
DA:1357,1
DA:1358,2
DA:1359,2
DA:1360,2
DA:1361,2
DA:1362,5
DA:1365,1
DA:1367,2
DA:1369,4
DA:1372,0
DA:1399,1
DA:1400,2
DA:1401,2
DA:1402,1
DA:1403,6
DA:1415,1
DA:1417,0
DA:1419,1
DA:1434,1
DA:1435,1
DA:1438,1
DA:1439,3
DA:1451,1
DA:1452,1
DA:1455,0
DA:1456,0
DA:1457,0
DA:1459,0
DA:1460,0
DA:1462,0
DA:1476,2
DA:1477,2
DA:1478,4
DA:1479,4
DA:1480,12
DA:1481,8
DA:1483,1
DA:1484,3
DA:1485,1
DA:1463,0
DA:1464,0
DA:1465,0
DA:1467,0
DA:1481,2
DA:1482,2
DA:1483,4
DA:1484,4
DA:1485,12
DA:1486,8
DA:1488,1
DA:1489,3
DA:1490,1
DA:1491,4
LF:482
DA:1493,1
DA:1495,1
DA:1496,4
LF:486
LH:246
end_of_record
+15 −14
Original line number Diff line number Diff line
%% Cell type:code id: tags:

``` rust
:dep pipe-lib = { path = "../pipe-lib" }
```

%% Cell type:code id: tags:

``` rust
:dep reqwest
:dep tokio = { version = "1.39.2", features = ["rt", "time", "rt-multi-thread"] }
:dep bytes
```

%% Cell type:code id: tags:

``` rust
use std::io::copy;
use std::io::Cursor;
use std::fs::File;
use std::path::PathBuf;
use tokio;

pub fn download_binary(url: String) -> Result<bytes::Bytes, String> {
    async fn download(url: String) -> Result<bytes::Bytes, String> {
pub fn download_binary(url: String, destination: PathBuf) -> Result<PathBuf, String> {
    async fn download(url: String, destination: PathBuf) -> Result<(), String> {
        let client = reqwest::Client::new();
        let response = client.get(url.clone()).send();
        let filename = PathBuf::from(url.clone()).file_name().unwrap().to_str().unwrap().to_string();
        match response.await {
            | Ok(data) => {
                match data.bytes().await {
                    | Ok(content) => {
                        let mut output = File::create(filename.clone()).unwrap();
                        copy(&mut Cursor::new(content.clone()), &mut output);
                        println!("Downloaded {filename}");
                        Ok(content)
                    },
                    | Err(_) => Err(format!("No content downloaded from {url}"))
            | Ok(data) => match data.bytes().await {
                | Ok(content) => {
                    let mut output = File::create(destination.join(filename.clone())).unwrap();
                    let _ = copy(&mut Cursor::new(content.clone()), &mut output);
                    debug!(filename = filename, "=> {} Downloaded", Label::output());
                    Ok(())
                }
                | Err(_) => Err(format!("No content downloaded from {url}")),
            },
            | Err(_) => Err(format!("Failed to download {url}"))
            | Err(_) => Err(format!("Failed to download {url}")),
        }
    }
    let runtime = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
    runtime.block_on(download(url.clone()))
    let _ = runtime.block_on(download(url.clone(), destination.clone()));
    let filename = PathBuf::from(url.clone()).file_name().unwrap().to_str().unwrap().to_string();
    Ok(destination.join(filename))
}
```

%% Cell type:code id: tags:

``` rust
let url = "https://code.ornl.gov/api/v4/projects/15383/packages/generic/x86_64-unknown-linux-gnu/v0.0.3/pipe".to_string();
let content = download_binary(url);
let content = download_binary(url, PathBuf::from("/root/dev/notebooks"));
println!("{:?}", content);
```

%% Output

    Downloaded pipe

%% Cell type:code id: tags:

``` rust
let path = "/tmp/pipe/iopbgzaGbV/pipe";
```
+76 −21
Original line number Diff line number Diff line
@@ -3,11 +3,15 @@ use color_eyre::eyre::Report;
use exitcode;
use nanoid::nanoid;
use pipe_lib::{
    get_parent, Command, Config, EnvironmentValue, Label, ModuleLanguage, ModuleType, ModuleUri, Script, ScriptTemplate, TemplateAttribute,
    download_binary, get_checksum, get_parent, Command, Config, EnvironmentValue, Label, ModuleLanguage, ModuleType, ModuleUri, Script,
    ScriptTemplate, TemplateAttribute,
};
use rayon::prelude::*;
use std::convert::TryFrom;
use std::env;
use std::fs;
#[cfg(any(unix, target_os = "wasi", target_os = "redox"))]
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
use tracing::{debug, error, info, warn};
use uriparse::Scheme;
@@ -15,6 +19,29 @@ use uriparse::URI;

const DEFAULT_CONFIG_NAME: &str = "config.json";

#[cfg(any(unix, target_os = "wasi", target_os = "redox"))]
fn make_executable(path: &PathBuf) {
    fs::set_permissions(path, fs::Permissions::from_mode(0o755)).unwrap();
}
#[cfg(windows)]
fn make_executable(path: &PathBuf) {
    // TODO: Add windows support...pass through?
}
fn check_requirements(config: &Config, skip: bool) {
    if !skip {
        config.requirements.iter().for_each(|name| {
            let command = Command::init().name(name.clone()).build();
            if !command.test() {
                error!(name, "=> {} Requirement not met", Label::not_found());
                std::process::exit(exitcode::UNAVAILABLE);
            }
        });
    } else {
        warn!("Skipping requirements verification");
    }
}
fn check_resources(_config: &Config, _skip: bool) {}

#[allow(clippy::too_many_arguments)]
pub fn run(
    config: &Option<PathBuf>,
@@ -31,7 +58,7 @@ pub fn run(
    match std::fs::create_dir_all(root.clone()) {
        | Ok(_) => {
            let path = root.clone().into_os_string().into_string().unwrap();
            debug!(path, "Created temporary directory");
            debug!(path, "=> {} Root directory", Label::using());
        }
        | Err(err) => error!("{}", err),
    }
@@ -49,34 +76,62 @@ pub fn run(
    };
    match Config::read(config_path) {
        | Some(cfg) => {
            // Check requirements
            if !skip_verify_requirements {
                cfg.requirements.iter().for_each(|name| {
                    let command = Command::init().name(name.clone()).build();
                    if !command.test() {
                        error!(name, "=> {} Requirement not met", Label::not_found());
                        std::process::exit(exitcode::UNAVAILABLE);
                    }
                });
            } else {
                warn!("Skipping requirements verification");
            }
            check_requirements(&cfg, skip_verify_requirements);
            check_resources(&cfg, false);
            //
            // Prepare modules
            //
            cfg.modules.par_iter().for_each(|module| match &module.module_type {
                | ModuleType::Binary { uri, .. } => match ModuleUri::from(uri) {
                | ModuleType::Binary { uri, checksum } => match ModuleUri::from(uri) {
                    | ModuleUri::Url(value) if !offline => match value.scheme() {
                        | Scheme::HTTP | Scheme::HTTPS | Scheme::SSH => {
                            let url = value.to_string();
                            debug!(module = module.name, url, "=> {} Remote binary", Label::found());
                            // TODO: Filter URL using whitelist
                            // TODO: Check that URL exists
                            // TODO: Download binary to temp directory
                            debug!(module = module.name, url, "=> {} Remote binary", Label::found());
                            if !dry_run {
                                let output = download_binary(url.clone(), root.clone());
                                if !skip_verify_checksum {
                                // TODO: Verify binary checksum
                                    let calculated_checksum = match output.clone() {
                                        | Ok(path) => get_checksum(path),
                                        | _ => "".to_string(),
                                    };
                                    match checksum {
                                        | Some(value) if value == &calculated_checksum => {
                                            info!(module = module.name, url, "=> {} Checksum verified", Label::pass());
                                            if let Ok(path) = output {
                                                make_executable(&path);
                                            }
                                        }
                                        | Some(_) => {
                                            error!(
                                                module = module.name,
                                                url,
                                                checksum,
                                                calculated = calculated_checksum,
                                                "=> {} Remote binary checksum does not match",
                                                Label::fail()
                                            );
                                            std::process::exit(exitcode::USAGE);
                                        }
                                        | None => {
                                            error!(
                                                module = module.name,
                                                url,
                                                checksum,
                                                "=> {} Remote binary checksum does not match",
                                                Label::fail()
                                            );
                                            std::process::exit(exitcode::UNAVAILABLE);
                                        }
                                    }
                                    // TODO: Verify binary is executable (https://docs.rs/is_executable/latest/is_executable/)
                                } else {
                                    warn!("Skipping checksum verification");
                                }
                            } else {
                                info!(module = module.name, url, "=> {} Download remote binary", Label::dry_run());
                            }
                            unimplemented!("Remote binary modules are not supported yet");
                        }
                        | _ => unimplemented!("Only HTTP, HTTPS, and SSH are currently supported for remote binary modules"),
+11 −6
Original line number Diff line number Diff line
@@ -640,7 +640,7 @@ impl Label {
    }
    pub fn fail() -> Styled<&'static &'static str> {
        let style = Style::new().white().on_red();
        " FAIL ".style(style)
        " FAIL ".style(style)
    }
    pub fn found() -> Styled<&'static &'static str> {
        let style = Style::new().green().on_default_color();
@@ -654,6 +654,10 @@ impl Label {
        let style = Style::new().cyan().dimmed().on_default_color();
        "OUTPUT".style(style)
    }
    pub fn pass() -> Styled<&'static &'static str> {
        let style = Style::new().green().on_default_color();
        "☑  PASS".style(style)
    }
    pub fn read() -> Styled<&'static &'static str> {
        let style = Style::new().green().on_default_color();
        "READ".style(style)
@@ -1317,15 +1321,15 @@ pub fn add_forward_slash(path: PathBuf) -> PathBuf {
        path.to_path_buf()
    }
}
pub fn download_binary(url: String) -> Result<(), String> {
    async fn download(url: String) -> Result<(), String> {
pub fn download_binary(url: String, destination: PathBuf) -> Result<PathBuf, String> {
    async fn download(url: String, destination: PathBuf) -> Result<(), String> {
        let client = reqwest::Client::new();
        let response = client.get(url.clone()).send();
        let filename = PathBuf::from(url.clone()).file_name().unwrap().to_str().unwrap().to_string();
        match response.await {
            | Ok(data) => match data.bytes().await {
                | Ok(content) => {
                    let mut output = File::create(filename.clone()).unwrap();
                    let mut output = File::create(destination.join(filename.clone())).unwrap();
                    let _ = copy(&mut Cursor::new(content.clone()), &mut output);
                    debug!(filename = filename, "=> {} Downloaded", Label::output());
                    Ok(())
@@ -1336,8 +1340,9 @@ pub fn download_binary(url: String) -> Result<(), String> {
        }
    }
    let runtime = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
    let _ = runtime.block_on(download(url.clone()));
    Ok(())
    let _ = runtime.block_on(download(url.clone(), destination.clone()));
    let filename = PathBuf::from(url.clone()).file_name().unwrap().to_str().unwrap().to_string();
    Ok(destination.join(filename))
}
/// Get SHA256 hash of a file
pub fn get_checksum(path: PathBuf) -> String {