Skip to content

Commit 519661d

Browse files
committed
Refactor move flake parsing into adapter file
1 parent 4f7e80c commit 519661d

File tree

3 files changed

+176
-163
lines changed

3 files changed

+176
-163
lines changed

src/cli.rs

Lines changed: 5 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ use clap::{Clap, ArgMatches, FromArgMatches};
1010

1111
use crate as deploy;
1212

13-
use self::deploy::{data, settings};
14-
use futures_util::stream::{StreamExt, TryStreamExt};
13+
use self::deploy::{data, settings, flake};
1514
use log::{debug, error, info, warn};
1615
use serde::Serialize;
1716
use std::process::Stdio;
@@ -107,163 +106,6 @@ async fn test_flake_support() -> Result<bool, std::io::Error> {
107106
.success())
108107
}
109108

110-
#[derive(Error, Debug)]
111-
pub enum CheckDeploymentError {
112-
#[error("Failed to execute Nix checking command: {0}")]
113-
NixCheck(#[from] std::io::Error),
114-
#[error("Nix checking command resulted in a bad exit code: {0:?}")]
115-
NixCheckExit(Option<i32>),
116-
}
117-
118-
async fn check_deployment(
119-
supports_flakes: bool,
120-
repo: &str,
121-
extra_build_args: &[String],
122-
) -> Result<(), CheckDeploymentError> {
123-
info!("Running checks for flake in {}", repo);
124-
125-
let mut check_command = match supports_flakes {
126-
true => Command::new("nix"),
127-
false => Command::new("nix-build"),
128-
};
129-
130-
match supports_flakes {
131-
true => {
132-
check_command.arg("flake").arg("check").arg(repo);
133-
}
134-
false => {
135-
check_command.arg("-E")
136-
.arg("--no-out-link")
137-
.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));
138-
}
139-
};
140-
141-
for extra_arg in extra_build_args {
142-
check_command.arg(extra_arg);
143-
}
144-
145-
let check_status = check_command.status().await?;
146-
147-
match check_status.code() {
148-
Some(0) => (),
149-
a => return Err(CheckDeploymentError::NixCheckExit(a)),
150-
};
151-
152-
Ok(())
153-
}
154-
155-
#[derive(Error, Debug)]
156-
pub enum GetDeploymentDataError {
157-
#[error("Failed to execute nix eval command: {0}")]
158-
NixEval(std::io::Error),
159-
#[error("Failed to read output from evaluation: {0}")]
160-
NixEvalOut(std::io::Error),
161-
#[error("Evaluation resulted in a bad exit code: {0:?}")]
162-
NixEvalExit(Option<i32>),
163-
#[error("Error converting evaluation output to utf8: {0}")]
164-
DecodeUtf8(#[from] std::string::FromUtf8Error),
165-
#[error("Error decoding the JSON from evaluation: {0}")]
166-
DecodeJson(#[from] serde_json::error::Error),
167-
#[error("Impossible happened: profile is set but node is not")]
168-
ProfileNoNode,
169-
}
170-
171-
/// Evaluates the Nix in the given `repo` and return the processed Data from it
172-
async fn get_deployment_data(
173-
supports_flakes: bool,
174-
flakes: &[data::Target],
175-
extra_build_args: &[String],
176-
) -> Result<Vec<settings::Root>, GetDeploymentDataError> {
177-
futures_util::stream::iter(flakes).then(|flake| async move {
178-
179-
info!("Evaluating flake in {}", flake.repo);
180-
181-
let mut c = if supports_flakes {
182-
Command::new("nix")
183-
} else {
184-
Command::new("nix-instantiate")
185-
};
186-
187-
if supports_flakes {
188-
c.arg("eval")
189-
.arg("--json")
190-
.arg(format!("{}#deploy", flake.repo))
191-
// We use --apply instead of --expr so that we don't have to deal with builtins.getFlake
192-
.arg("--apply");
193-
match (&flake.node, &flake.profile) {
194-
(Some(node), Some(profile)) => {
195-
// Ignore all nodes and all profiles but the one we're evaluating
196-
c.arg(format!(
197-
r#"
198-
deploy:
199-
(deploy // {{
200-
nodes = {{
201-
"{0}" = deploy.nodes."{0}" // {{
202-
profiles = {{
203-
inherit (deploy.nodes."{0}".profiles) "{1}";
204-
}};
205-
}};
206-
}};
207-
}})
208-
"#,
209-
node, profile
210-
))
211-
}
212-
(Some(node), None) => {
213-
// Ignore all nodes but the one we're evaluating
214-
c.arg(format!(
215-
r#"
216-
deploy:
217-
(deploy // {{
218-
nodes = {{
219-
inherit (deploy.nodes) "{}";
220-
}};
221-
}})
222-
"#,
223-
node
224-
))
225-
}
226-
(None, None) => {
227-
// We need to evaluate all profiles of all nodes anyway, so just do it strictly
228-
c.arg("deploy: deploy")
229-
}
230-
(None, Some(_)) => return Err(GetDeploymentDataError::ProfileNoNode),
231-
}
232-
} else {
233-
c
234-
.arg("--strict")
235-
.arg("--read-write-mode")
236-
.arg("--json")
237-
.arg("--eval")
238-
.arg("-E")
239-
.arg(format!("let r = import {}/.; in if builtins.isFunction r then (r {{}}).deploy else r.deploy", flake.repo))
240-
};
241-
242-
for extra_arg in extra_build_args {
243-
c.arg(extra_arg);
244-
}
245-
246-
let build_child = c
247-
.stdout(Stdio::piped())
248-
.spawn()
249-
.map_err(GetDeploymentDataError::NixEval)?;
250-
251-
let build_output = build_child
252-
.wait_with_output()
253-
.await
254-
.map_err(GetDeploymentDataError::NixEvalOut)?;
255-
256-
match build_output.status.code() {
257-
Some(0) => (),
258-
a => return Err(GetDeploymentDataError::NixEvalExit(a)),
259-
};
260-
261-
let data_json = String::from_utf8(build_output.stdout)?;
262-
263-
Ok(serde_json::from_str(&data_json)?)
264-
}).try_collect().await
265-
}
266-
267109
#[derive(Serialize)]
268110
struct PromptPart<'a> {
269111
user: &'a str,
@@ -596,9 +438,9 @@ pub enum RunError {
596438
#[error("Failed to test for flake support: {0}")]
597439
FlakeTest(std::io::Error),
598440
#[error("Failed to check deployment: {0}")]
599-
CheckDeployment(#[from] CheckDeploymentError),
441+
CheckDeployment(#[from] flake::CheckDeploymentError),
600442
#[error("Failed to evaluate deployment data: {0}")]
601-
GetDeploymentData(#[from] GetDeploymentDataError),
443+
GetDeploymentData(#[from] flake::GetDeploymentDataError),
602444
#[error("Error parsing flake: {0}")]
603445
ParseFlake(#[from] data::ParseTargetError),
604446
#[error("Error initiating logger: {0}")]
@@ -650,11 +492,11 @@ pub async fn run(args: Option<&ArgMatches>) -> Result<(), RunError> {
650492

651493
if !opts.skip_checks {
652494
for deploy_target in deploy_targets.iter() {
653-
check_deployment(supports_flakes, &deploy_target.repo, &opts.extra_build_args).await?;
495+
flake::check_deployment(supports_flakes, &deploy_target.repo, &opts.extra_build_args).await?;
654496
}
655497
}
656498
let result_path = opts.result_path.as_deref();
657-
let data = get_deployment_data(supports_flakes, &deploy_targets, &opts.extra_build_args).await?;
499+
let data = flake::get_deployment_data(supports_flakes, &deploy_targets, &opts.extra_build_args).await?;
658500
run_deploy(
659501
deploy_targets,
660502
data,

src/flake.rs

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// SPDX-FileCopyrightText: 2020 Serokell <https://serokell.io/>
2+
// SPDX-FileCopyrightText: 2021 Yannik Sander <contact@ysndr.de>
3+
//
4+
// SPDX-License-Identifier: MPL-2.0
5+
6+
use crate as deploy;
7+
8+
use self::deploy::{data, settings};
9+
use log::{error, info};
10+
use std::process::Stdio;
11+
use futures_util::stream::{StreamExt, TryStreamExt};
12+
use thiserror::Error;
13+
use tokio::process::Command;
14+
15+
#[derive(Error, Debug)]
16+
pub enum CheckDeploymentError {
17+
#[error("Failed to execute Nix checking command: {0}")]
18+
NixCheck(#[from] std::io::Error),
19+
#[error("Nix checking command resulted in a bad exit code: {0:?}")]
20+
NixCheckExit(Option<i32>),
21+
}
22+
23+
pub async fn check_deployment(
24+
supports_flakes: bool,
25+
repo: &str,
26+
extra_build_args: &[String],
27+
) -> Result<(), CheckDeploymentError> {
28+
info!("Running checks for flake in {}", repo);
29+
30+
let mut check_command = match supports_flakes {
31+
true => Command::new("nix"),
32+
false => Command::new("nix-build"),
33+
};
34+
35+
match supports_flakes {
36+
true => {
37+
check_command.arg("flake").arg("check").arg(repo);
38+
}
39+
false => {
40+
check_command.arg("-E")
41+
.arg("--no-out-link")
42+
.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));
43+
}
44+
};
45+
46+
for extra_arg in extra_build_args {
47+
check_command.arg(extra_arg);
48+
}
49+
50+
let check_status = check_command.status().await?;
51+
52+
match check_status.code() {
53+
Some(0) => (),
54+
a => return Err(CheckDeploymentError::NixCheckExit(a)),
55+
};
56+
57+
Ok(())
58+
}
59+
60+
#[derive(Error, Debug)]
61+
pub enum GetDeploymentDataError {
62+
#[error("Failed to execute nix eval command: {0}")]
63+
NixEval(std::io::Error),
64+
#[error("Failed to read output from evaluation: {0}")]
65+
NixEvalOut(std::io::Error),
66+
#[error("Evaluation resulted in a bad exit code: {0:?}")]
67+
NixEvalExit(Option<i32>),
68+
#[error("Error converting evaluation output to utf8: {0}")]
69+
DecodeUtf8(#[from] std::string::FromUtf8Error),
70+
#[error("Error decoding the JSON from evaluation: {0}")]
71+
DecodeJson(#[from] serde_json::error::Error),
72+
#[error("Impossible happened: profile is set but node is not")]
73+
ProfileNoNode,
74+
}
75+
76+
/// Evaluates the Nix in the given `repo` and return the processed Data from it
77+
pub async fn get_deployment_data(
78+
supports_flakes: bool,
79+
flakes: &[data::Target],
80+
extra_build_args: &[String],
81+
) -> Result<Vec<settings::Root>, GetDeploymentDataError> {
82+
futures_util::stream::iter(flakes).then(|flake| async move {
83+
84+
info!("Evaluating flake in {}", flake.repo);
85+
86+
let mut c = if supports_flakes {
87+
Command::new("nix")
88+
} else {
89+
Command::new("nix-instantiate")
90+
};
91+
92+
if supports_flakes {
93+
c.arg("eval")
94+
.arg("--json")
95+
.arg(format!("{}#deploy", flake.repo))
96+
// We use --apply instead of --expr so that we don't have to deal with builtins.getFlake
97+
.arg("--apply");
98+
match (&flake.node, &flake.profile) {
99+
(Some(node), Some(profile)) => {
100+
// Ignore all nodes and all profiles but the one we're evaluating
101+
c.arg(format!(
102+
r#"
103+
deploy:
104+
(deploy // {{
105+
nodes = {{
106+
"{0}" = deploy.nodes."{0}" // {{
107+
profiles = {{
108+
inherit (deploy.nodes."{0}".profiles) "{1}";
109+
}};
110+
}};
111+
}};
112+
}})
113+
"#,
114+
node, profile
115+
))
116+
}
117+
(Some(node), None) => {
118+
// Ignore all nodes but the one we're evaluating
119+
c.arg(format!(
120+
r#"
121+
deploy:
122+
(deploy // {{
123+
nodes = {{
124+
inherit (deploy.nodes) "{}";
125+
}};
126+
}})
127+
"#,
128+
node
129+
))
130+
}
131+
(None, None) => {
132+
// We need to evaluate all profiles of all nodes anyway, so just do it strictly
133+
c.arg("deploy: deploy")
134+
}
135+
(None, Some(_)) => return Err(GetDeploymentDataError::ProfileNoNode),
136+
}
137+
} else {
138+
c
139+
.arg("--strict")
140+
.arg("--read-write-mode")
141+
.arg("--json")
142+
.arg("--eval")
143+
.arg("-E")
144+
.arg(format!("let r = import {}/.; in if builtins.isFunction r then (r {{}}).deploy else r.deploy", flake.repo))
145+
};
146+
147+
for extra_arg in extra_build_args {
148+
c.arg(extra_arg);
149+
}
150+
151+
let build_child = c
152+
.stdout(Stdio::piped())
153+
.spawn()
154+
.map_err(GetDeploymentDataError::NixEval)?;
155+
156+
let build_output = build_child
157+
.wait_with_output()
158+
.await
159+
.map_err(GetDeploymentDataError::NixEvalOut)?;
160+
161+
match build_output.status.code() {
162+
Some(0) => (),
163+
a => return Err(GetDeploymentDataError::NixEvalExit(a)),
164+
};
165+
166+
let data_json = String::from_utf8(build_output.stdout)?;
167+
168+
Ok(serde_json::from_str(&data_json)?)
169+
}).try_collect().await
170+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ pub fn init_logger(
141141

142142
pub mod settings;
143143
pub mod data;
144+
pub mod flake;
144145
pub mod deploy;
145146
pub mod push;
146147
pub mod cli;

0 commit comments

Comments
 (0)