Skip to content

Commit

Permalink
Fix install.environment silently ignoring configuration
Browse files Browse the repository at this point in the history
This came up today when someone tried using:

```
install:
  environment:
    - append: VAR_NAME
      value: value
      priority: 1
```

The existing code first parses the `append` op but then replaces it with
a `priority` op, discarding the `append`.

The proper recipe content is:

```
install:
  environment:
    - append: VAR_NAME
      value: value
    - priority: 1
```

Now this is detected as an error instead of behaving with unexpected
results.

Signed-off-by: J Robert Ray <jrray@jrray.org>
  • Loading branch information
jrray committed Jun 18, 2024
1 parent 59195e7 commit 6bfa1cc
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 1 deletion.
28 changes: 27 additions & 1 deletion crates/spk-schema/src/environ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ const OP_SET: &str = "set";
const OP_NAMES: &[&str] = &[OP_APPEND, OP_COMMENT, OP_PREPEND, OP_SET];

/// The set of operation types for use in deserialization
#[derive(Copy, Clone, Debug, PartialEq)]
#[derive(Copy, Clone, Debug, PartialEq, strum::Display)]
#[strum(serialize_all = "lowercase")]
pub enum OpKind {
Append,
Comment,
Expand Down Expand Up @@ -197,30 +198,55 @@ impl<'de> Deserialize<'de> for EnvOp {
while let Some(key) = map.next_key::<String>()? {
match key.as_str() {
OP_PREPEND => {
if let Some((existing_op, _)) = &self.op_and_var {
return Err(serde::de::Error::custom(format!(
"encountered {key} but operation already defined as {existing_op}",
)));
}
self.op_and_var = Some((
OpKind::Prepend,
ConfKind::Operation(map.next_value::<Stringified>()?.0),
));
}
OP_PRIORITY => {
if let Some((existing_op, _)) = &self.op_and_var {
return Err(serde::de::Error::custom(format!(
"encountered {key} but operation already defined as {existing_op}",
)));
}
self.op_and_var = Some((
OpKind::Priority,
ConfKind::Priority(map.next_value::<u8>()?),
));
}
OP_COMMENT => {
if let Some((existing_op, _)) = &self.op_and_var {
return Err(serde::de::Error::custom(format!(
"encountered {key} but operation already defined as {existing_op}",
)));
}
self.op_and_var = Some((
OpKind::Comment,
ConfKind::Operation(map.next_value::<Stringified>()?.0),
));
}
OP_APPEND => {
if let Some((existing_op, _)) = &self.op_and_var {
return Err(serde::de::Error::custom(format!(
"encountered {key} but operation already defined as {existing_op}",
)));
}
self.op_and_var = Some((
OpKind::Append,
ConfKind::Operation(map.next_value::<Stringified>()?.0),
));
}
OP_SET => {
if let Some((existing_op, _)) = &self.op_and_var {
return Err(serde::de::Error::custom(format!(
"encountered {key} but operation already defined as {existing_op}",
)));
}
self.op_and_var = Some((
OpKind::Set,
ConfKind::Operation(map.next_value::<Stringified>()?.0),
Expand Down
9 changes: 9 additions & 0 deletions crates/spk-schema/src/environ_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,12 @@ fn test_yaml_round_trip(#[case] op: &str) {
let op2: EnvOp = serde_yaml::from_str(&yaml).unwrap();
assert_eq!(op2, op, "should be the same after sending through yaml");
}

/// Test that ambiguous/overlapping op definitions cause a parse error instead
/// of silently ignoring some of the configuration.
#[rstest]
#[case("{set: SPK_TEST_VAR, append: SPK_TEST_VAR, value: simple")]
fn test_multiple_ops_cause_error(#[case] op: &str) {
let result: Result<EnvOp, _> = serde_yaml::from_str(op);
assert!(result.is_err(), "should fail to parse multiple ops");
}

0 comments on commit 6bfa1cc

Please sign in to comment.