Skip to content

Commit 1c03e43

Browse files
authored
Bugfix: get_unresolved_requests() and incompatible requests (#1138)
Removes unwrap, and changes function to return a Result for better error handling Signed-off-by: David Gilligan-Cook <[email protected]>
1 parent c95d54e commit 1c03e43

File tree

6 files changed

+128
-16
lines changed

6 files changed

+128
-16
lines changed

crates/spk-cli/group1/src/cmd_bake.rs

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ use spk_schema::ident::RequestedBy;
1111
use spk_schema::Package;
1212
use spk_solve::solution::{get_spfs_layers_to_packages, LayerPackageAndComponents, PackageSource};
1313

14+
#[cfg(test)]
15+
#[path = "./cmd_bake_test.rs"]
16+
mod cmd_bake_test;
17+
1418
// Verbosity level above which repo and component names will be
1519
// included in the package display values.
1620
const NO_VERBOSITY: u8 = 0;
+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Copyright (c) Contributors to the SPK project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
// https://github.com/spkenv/spk
4+
5+
use clap::Parser;
6+
use rstest::rstest;
7+
use spfs::config::Remote;
8+
use spfs::RemoteAddress;
9+
use spk_cli_common::Run;
10+
use spk_solve::{recipe, spec, Component};
11+
use spk_storage::fixtures::{empty_layer_digest, spfs_runtime, spfsrepo};
12+
13+
use super::Bake;
14+
15+
#[derive(Parser)]
16+
struct Opt {
17+
#[clap(flatten)]
18+
bake: Bake,
19+
}
20+
21+
#[rstest]
22+
#[tokio::test]
23+
async fn test_bake() {
24+
// Test the bake command runs
25+
let mut rt = spfs_runtime().await;
26+
let remote_repo = spfsrepo().await;
27+
28+
// Populate the "origin" repo with one package.
29+
// The "local" repo is empty.
30+
rt.add_remote_repo(
31+
"origin",
32+
Remote::Address(RemoteAddress {
33+
address: remote_repo.address().clone(),
34+
}),
35+
)
36+
.unwrap();
37+
38+
let recipe = recipe!({"pkg": "my-pkg/1.0.1"});
39+
remote_repo.publish_recipe(&recipe).await.unwrap();
40+
let spec = spec!({"pkg": "my-pkg/1.0.1/ZPGKGOTY"});
41+
remote_repo
42+
.publish_package(
43+
&spec,
44+
&vec![(Component::Run, empty_layer_digest())]
45+
.into_iter()
46+
.collect(),
47+
)
48+
.await
49+
.unwrap();
50+
51+
// Test a basic bake
52+
let mut opt = Opt::try_parse_from(["bake", "--no-runtime", "my-pkg:run"]).unwrap();
53+
let result = opt.bake.run().await.unwrap();
54+
assert_eq!(result, 0);
55+
}
56+
57+
#[rstest]
58+
#[tokio::test]
59+
async fn test_bake_incompatible_merged_request() {
60+
// Test bake with an incompatible set of requests
61+
let mut rt = spfs_runtime().await;
62+
let remote_repo = spfsrepo().await;
63+
64+
// Populate the "origin" repo with one package.
65+
// The "local" repo is empty.
66+
rt.add_remote_repo(
67+
"origin",
68+
Remote::Address(RemoteAddress {
69+
address: remote_repo.address().clone(),
70+
}),
71+
)
72+
.unwrap();
73+
74+
let recipe = recipe!({"pkg": "my-pkg/1.0.33+r.1"});
75+
remote_repo.publish_recipe(&recipe).await.unwrap();
76+
let spec = spec!({"pkg": "my-pkg/1.0.33+r.1/ZPGKGOTY"});
77+
remote_repo
78+
.publish_package(
79+
&spec,
80+
&vec![(Component::Run, empty_layer_digest())]
81+
.into_iter()
82+
.collect(),
83+
)
84+
.await
85+
.unwrap();
86+
87+
// Test bake command with 2 incompatible requests. This should
88+
// not panic, it should error out
89+
let mut opt = Opt::try_parse_from([
90+
"bake",
91+
"--no-runtime",
92+
"my-pkg:run/==1.0.33+r.1/ZPGKGOTY",
93+
"my-pkg:run/=1.0.99",
94+
])
95+
.unwrap();
96+
let result = opt.bake.run().await;
97+
println!("bake run result: {result:?}");
98+
99+
match result {
100+
Err(err) => {
101+
println!("Bake errored with: {err}");
102+
}
103+
Ok(_value) => {
104+
panic!("Incompatible requests for same package should cause bake to error");
105+
}
106+
}
107+
}

crates/spk-solve/crates/graph/src/graph.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -1374,22 +1374,20 @@ impl State {
13741374

13751375
/// Get a mapping of pkg name -> merged request for the unresolved
13761376
/// PkgRequests in this state
1377-
pub fn get_unresolved_requests(&self) -> &HashMap<PkgNameBuf, PkgRequest> {
1378-
self.cached_unresolved_pkg_requests.get_or_init(|| {
1377+
pub fn get_unresolved_requests(&self) -> Result<&HashMap<PkgNameBuf, PkgRequest>> {
1378+
self.cached_unresolved_pkg_requests.get_or_try_init(|| {
13791379
let mut unresolved: HashMap<PkgNameBuf, PkgRequest> = HashMap::new();
13801380

13811381
for req in self.pkg_requests.iter() {
13821382
if unresolved.contains_key(&req.pkg.name) {
13831383
continue;
13841384
}
13851385
if self.get_current_resolve(&req.pkg.name).is_err() {
1386-
unresolved.insert(
1387-
req.pkg.name.clone(),
1388-
self.get_merged_request(&req.pkg.name).unwrap(),
1389-
);
1386+
let merged = self.get_merged_request(&req.pkg.name)?;
1387+
unresolved.insert(req.pkg.name.clone(), merged);
13901388
}
13911389
}
1392-
unresolved
1390+
Ok(unresolved)
13931391
})
13941392
}
13951393

crates/spk-solve/src/io.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,9 @@ where
243243
}
244244
}
245245

246-
fn show_unresolved_requests(&self, state: &Arc<State>) {
246+
fn show_unresolved_requests(&self, state: &Arc<State>) -> Result<()> {
247247
let unresolved_requests = state
248-
.get_unresolved_requests()
248+
.get_unresolved_requests()?
249249
.iter()
250250
.map(|(n, r)| {
251251
r.format_request(
@@ -264,6 +264,7 @@ where
264264
unresolved_requests.join("\n "),
265265
unresolved_requests.len()
266266
);
267+
Ok(())
267268
}
268269

269270
fn show_var_requests(&self, state: &Arc<State>) {
@@ -290,11 +291,12 @@ where
290291
);
291292
}
292293

293-
fn show_state(&self, state: &Arc<State>) {
294+
fn show_state(&self, state: &Arc<State>) -> Result<()> {
294295
self.show_resolved_packages(state);
295-
self.show_unresolved_requests(state);
296+
self.show_unresolved_requests(state)?;
296297
self.show_var_requests(state);
297298
self.show_options(state);
299+
Ok(())
298300
}
299301

300302
fn show_full_menu(&self, prompt_prefix: &str) {
@@ -343,10 +345,10 @@ where
343345
match selection {
344346
'?' => self.show_full_menu(&prompt_prefix),
345347
'r' => self.show_resolved_packages(state),
346-
'u' => self.show_unresolved_requests(state),
348+
'u' => self.show_unresolved_requests(state)?,
347349
'v' => self.show_var_requests(state),
348350
'o' => self.show_options(state),
349-
's' | 'a' => self.show_state(state),
351+
's' | 'a' => self.show_state(state)?,
350352
'c' => {
351353
self.remove_step_and_stop_setting();
352354
break;

crates/spk-solve/src/solver.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ impl Solver {
555555
let builds_with_impossible_requests = if self.impossible_checks.use_in_build_keys {
556556
let impossible_check_start = Instant::now();
557557
let start_number = self.request_validator.num_build_specs_read();
558-
let unresolved = node.state.get_unresolved_requests();
558+
let unresolved = node.state.get_unresolved_requests()?;
559559

560560
let problematic_builds = self
561561
.check_builds_for_impossible_requests(unresolved, builds.clone())
@@ -608,7 +608,7 @@ impl Solver {
608608
// are used to check the new requests this
609609
// build would add, if it was used to
610610
// resolve the current request.
611-
let unresolved = node.state.get_unresolved_requests();
611+
let unresolved = node.state.get_unresolved_requests()?;
612612
let compat = self
613613
.check_requirements_for_impossible_requests(
614614
&spec, unresolved,
@@ -844,7 +844,7 @@ impl Solver {
844844

845845
let tasks = FuturesUnordered::new();
846846

847-
let initial_requests = initial_state.get_unresolved_requests();
847+
let initial_requests = initial_state.get_unresolved_requests()?;
848848
for (count, req) in initial_requests.values().enumerate() {
849849
// Have to make a dummy spec for an "initialrequest"
850850
// package to interact with the request_validator's

cspell.json

+1
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,7 @@
785785
"YYYNNNNNNN",
786786
"YYYNNNYYYY",
787787
"ZLMZGDCVUOL",
788+
"ZPGKGOTY",
788789
"ZWOIF"
789790
],
790791
"enabled": true

0 commit comments

Comments
 (0)