Skip to content

Commit

Permalink
Refactor move flake parsing into adapter file
Browse files Browse the repository at this point in the history
  • Loading branch information
blaggacao committed Aug 26, 2021
1 parent a10cc65 commit aff6f1f
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 160 deletions.
165 changes: 5 additions & 160 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ use clap::{ArgMatches, Clap, FromArgMatches};

use crate as deploy;

use self::deploy::{data, settings};
use futures_util::stream::{StreamExt, TryStreamExt};
use self::deploy::{data, settings, flake};
use log::{debug, error, info, warn};
use serde::Serialize;
use std::process::Stdio;
Expand Down Expand Up @@ -107,160 +106,6 @@ async fn test_flake_support() -> Result<bool, std::io::Error> {
.success())
}

#[derive(Error, Debug)]
pub enum CheckDeploymentError {
#[error("Failed to execute Nix checking command: {0}")]
NixCheck(#[from] std::io::Error),
#[error("Nix checking command resulted in a bad exit code: {0:?}")]
NixCheckExit(Option<i32>),
}

async fn check_deployment(
supports_flakes: bool,
repo: &str,
extra_build_args: &[String],
) -> Result<(), CheckDeploymentError> {
info!("Running checks for flake in {}", repo);

let mut check_command = match supports_flakes {
true => Command::new("nix"),
false => Command::new("nix-build"),
};

if supports_flakes {
check_command.arg("flake").arg("check").arg(repo);
} else {
check_command.arg("-E")
.arg("--no-out-link")
.arg(format!("let r = import {}/.; x = (if builtins.isFunction r then (r {{}}) else r); in if x ? checks then x.checks.${{builtins.currentSystem}} else {{}}", repo));
}

for extra_arg in extra_build_args {
check_command.arg(extra_arg);
}

let check_status = check_command.status().await?;

match check_status.code() {
Some(0) => (),
a => return Err(CheckDeploymentError::NixCheckExit(a)),
};

Ok(())
}

#[derive(Error, Debug)]
pub enum GetDeploymentDataError {
#[error("Failed to execute nix eval command: {0}")]
NixEval(std::io::Error),
#[error("Failed to read output from evaluation: {0}")]
NixEvalOut(std::io::Error),
#[error("Evaluation resulted in a bad exit code: {0:?}")]
NixEvalExit(Option<i32>),
#[error("Error converting evaluation output to utf8: {0}")]
DecodeUtf8(#[from] std::string::FromUtf8Error),
#[error("Error decoding the JSON from evaluation: {0}")]
DecodeJson(#[from] serde_json::error::Error),
#[error("Impossible happened: profile is set but node is not")]
ProfileNoNode,
}

/// Evaluates the Nix in the given `repo` and return the processed Data from it
async fn get_deployment_data(
supports_flakes: bool,
flakes: &[data::Target],
extra_build_args: &[String],
) -> Result<Vec<settings::Root>, GetDeploymentDataError> {
futures_util::stream::iter(flakes).then(|flake| async move {

info!("Evaluating flake in {}", flake.repo);

let mut c = if supports_flakes {
Command::new("nix")
} else {
Command::new("nix-instantiate")
};

if supports_flakes {
c.arg("eval")
.arg("--json")
.arg(format!("{}#deploy", flake.repo))
// We use --apply instead of --expr so that we don't have to deal with builtins.getFlake
.arg("--apply");
match (&flake.node, &flake.profile) {
(Some(node), Some(profile)) => {
// Ignore all nodes and all profiles but the one we're evaluating
c.arg(format!(
r#"
deploy:
(deploy // {{
nodes = {{
"{0}" = deploy.nodes."{0}" // {{
profiles = {{
inherit (deploy.nodes."{0}".profiles) "{1}";
}};
}};
}};
}})
"#,
node, profile
))
}
(Some(node), None) => {
// Ignore all nodes but the one we're evaluating
c.arg(format!(
r#"
deploy:
(deploy // {{
nodes = {{
inherit (deploy.nodes) "{}";
}};
}})
"#,
node
))
}
(None, None) => {
// We need to evaluate all profiles of all nodes anyway, so just do it strictly
c.arg("deploy: deploy")
}
(None, Some(_)) => return Err(GetDeploymentDataError::ProfileNoNode),
}
} else {
c
.arg("--strict")
.arg("--read-write-mode")
.arg("--json")
.arg("--eval")
.arg("-E")
.arg(format!("let r = import {}/.; in if builtins.isFunction r then (r {{}}).deploy else r.deploy", flake.repo))
};

for extra_arg in extra_build_args {
c.arg(extra_arg);
}

let build_child = c
.stdout(Stdio::piped())
.spawn()
.map_err(GetDeploymentDataError::NixEval)?;

let build_output = build_child
.wait_with_output()
.await
.map_err(GetDeploymentDataError::NixEvalOut)?;

match build_output.status.code() {
Some(0) => (),
a => return Err(GetDeploymentDataError::NixEvalExit(a)),
};

let data_json = String::from_utf8(build_output.stdout)?;

Ok(serde_json::from_str(&data_json)?)
}).try_collect().await
}

#[derive(Serialize)]
struct PromptPart<'a> {
user: &'a str,
Expand Down Expand Up @@ -591,9 +436,9 @@ pub enum RunError {
#[error("Failed to test for flake support: {0}")]
FlakeTest(std::io::Error),
#[error("Failed to check deployment: {0}")]
CheckDeployment(#[from] CheckDeploymentError),
CheckDeployment(#[from] flake::CheckDeploymentError),
#[error("Failed to evaluate deployment data: {0}")]
GetDeploymentData(#[from] GetDeploymentDataError),
GetDeploymentData(#[from] flake::GetDeploymentDataError),
#[error("Error parsing flake: {0}")]
ParseFlake(#[from] data::ParseTargetError),
#[error("Error initiating logger: {0}")]
Expand Down Expand Up @@ -645,11 +490,11 @@ pub async fn run(args: Option<&ArgMatches>) -> Result<(), RunError> {

if !opts.skip_checks {
for deploy_target in deploy_targets.iter() {
check_deployment(supports_flakes, &deploy_target.repo, &opts.extra_build_args).await?;
flake::check_deployment(supports_flakes, &deploy_target.repo, &opts.extra_build_args).await?;
}
}
let result_path = opts.result_path.as_deref();
let data = get_deployment_data(supports_flakes, &deploy_targets, &opts.extra_build_args).await?;
let data = flake::get_deployment_data(supports_flakes, &deploy_targets, &opts.extra_build_args).await?;
run_deploy(
deploy_targets,
data,
Expand Down
167 changes: 167 additions & 0 deletions src/flake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// SPDX-FileCopyrightText: 2020 Serokell <https://serokell.io/>
// SPDX-FileCopyrightText: 2021 Yannik Sander <[email protected]>
//
// SPDX-License-Identifier: MPL-2.0

use crate as deploy;

use self::deploy::{data, settings};
use log::{error, info};
use std::process::Stdio;
use futures_util::stream::{StreamExt, TryStreamExt};
use thiserror::Error;
use tokio::process::Command;

#[derive(Error, Debug)]
pub enum CheckDeploymentError {
#[error("Failed to execute Nix checking command: {0}")]
NixCheck(#[from] std::io::Error),
#[error("Nix checking command resulted in a bad exit code: {0:?}")]
NixCheckExit(Option<i32>),
}

pub async fn check_deployment(
supports_flakes: bool,
repo: &str,
extra_build_args: &[String],
) -> Result<(), CheckDeploymentError> {
info!("Running checks for flake in {}", repo);

let mut check_command = match supports_flakes {
true => Command::new("nix"),
false => Command::new("nix-build"),
};

if supports_flakes {
check_command.arg("flake").arg("check").arg(repo);
} else {
check_command.arg("-E")
.arg("--no-out-link")
.arg(format!("let r = import {}/.; x = (if builtins.isFunction r then (r {{}}) else r); in if x ? checks then x.checks.${{builtins.currentSystem}} else {{}}", repo));
};

for extra_arg in extra_build_args {
check_command.arg(extra_arg);
}

let check_status = check_command.status().await?;

match check_status.code() {
Some(0) => (),
a => return Err(CheckDeploymentError::NixCheckExit(a)),
};

Ok(())
}

#[derive(Error, Debug)]
pub enum GetDeploymentDataError {
#[error("Failed to execute nix eval command: {0}")]
NixEval(std::io::Error),
#[error("Failed to read output from evaluation: {0}")]
NixEvalOut(std::io::Error),
#[error("Evaluation resulted in a bad exit code: {0:?}")]
NixEvalExit(Option<i32>),
#[error("Error converting evaluation output to utf8: {0}")]
DecodeUtf8(#[from] std::string::FromUtf8Error),
#[error("Error decoding the JSON from evaluation: {0}")]
DecodeJson(#[from] serde_json::error::Error),
#[error("Impossible happened: profile is set but node is not")]
ProfileNoNode,
}

/// Evaluates the Nix in the given `repo` and return the processed Data from it
pub async fn get_deployment_data(
supports_flakes: bool,
flakes: &[data::Target],
extra_build_args: &[String],
) -> Result<Vec<settings::Root>, GetDeploymentDataError> {
futures_util::stream::iter(flakes).then(|flake| async move {

info!("Evaluating flake in {}", flake.repo);

let mut c = if supports_flakes {
Command::new("nix")
} else {
Command::new("nix-instantiate")
};

if supports_flakes {
c.arg("eval")
.arg("--json")
.arg(format!("{}#deploy", flake.repo))
// We use --apply instead of --expr so that we don't have to deal with builtins.getFlake
.arg("--apply");
match (&flake.node, &flake.profile) {
(Some(node), Some(profile)) => {
// Ignore all nodes and all profiles but the one we're evaluating
c.arg(format!(
r#"
deploy:
(deploy // {{
nodes = {{
"{0}" = deploy.nodes."{0}" // {{
profiles = {{
inherit (deploy.nodes."{0}".profiles) "{1}";
}};
}};
}};
}})
"#,
node, profile
))
}
(Some(node), None) => {
// Ignore all nodes but the one we're evaluating
c.arg(format!(
r#"
deploy:
(deploy // {{
nodes = {{
inherit (deploy.nodes) "{}";
}};
}})
"#,
node
))
}
(None, None) => {
// We need to evaluate all profiles of all nodes anyway, so just do it strictly
c.arg("deploy: deploy")
}
(None, Some(_)) => return Err(GetDeploymentDataError::ProfileNoNode),
}
} else {
c
.arg("--strict")
.arg("--read-write-mode")
.arg("--json")
.arg("--eval")
.arg("-E")
.arg(format!("let r = import {}/.; in if builtins.isFunction r then (r {{}}).deploy else r.deploy", flake.repo))
};

for extra_arg in extra_build_args {
c.arg(extra_arg);
}

let build_child = c
.stdout(Stdio::piped())
.spawn()
.map_err(GetDeploymentDataError::NixEval)?;

let build_output = build_child
.wait_with_output()
.await
.map_err(GetDeploymentDataError::NixEvalOut)?;

match build_output.status.code() {
Some(0) => (),
a => return Err(GetDeploymentDataError::NixEvalExit(a)),
};

let data_json = String::from_utf8(build_output.stdout)?;

Ok(serde_json::from_str(&data_json)?)
}).try_collect().await
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ pub fn init_logger(

pub mod settings;
pub mod data;
pub mod flake;
pub mod deploy;
pub mod push;
pub mod cli;

0 comments on commit aff6f1f

Please sign in to comment.