about summary refs log tree commit diff
path: root/tvix/derivation/src/validate.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tvix/derivation/src/validate.rs')
-rw-r--r--tvix/derivation/src/validate.rs104
1 files changed, 104 insertions, 0 deletions
diff --git a/tvix/derivation/src/validate.rs b/tvix/derivation/src/validate.rs
new file mode 100644
index 000000000000..e0f62a323fcf
--- /dev/null
+++ b/tvix/derivation/src/validate.rs
@@ -0,0 +1,104 @@
+use crate::{derivation::Derivation, write::DOT_FILE_EXT};
+use anyhow::bail;
+use tvix_store::nixpath::NixPath;
+
+impl Derivation {
+    /// validate ensures a Derivation struct is properly populated,
+    /// and returns an error if not.
+    /// TODO(flokli): make this proper errors
+    pub fn validate(&self) -> anyhow::Result<()> {
+        // Ensure the number of outputs is > 1
+        if self.outputs.is_empty() {
+            bail!("0 outputs");
+        }
+
+        // Validate all outputs
+        for (output_name, output) in &self.outputs {
+            if output_name.is_empty() {
+                bail!("output_name from outputs may not be empty")
+            }
+
+            if output.is_fixed() {
+                if self.outputs.len() != 1 {
+                    bail!("encountered fixed-output, but there's more than 1 output in total");
+                }
+                if output_name != "out" {
+                    bail!("the fixed-output output name must be called 'out'");
+                }
+
+                break;
+            }
+
+            output.validate()?;
+        }
+
+        // Validate all input_derivations
+        for (input_derivation_path, output_names) in &self.input_derivations {
+            // Validate input_derivation_path
+            NixPath::from_absolute_path(input_derivation_path)?;
+            if !input_derivation_path.ends_with(DOT_FILE_EXT) {
+                bail!(
+                    "derivation {} does not end with .drv",
+                    input_derivation_path
+                );
+            }
+
+            if output_names.is_empty() {
+                bail!(
+                    "output_names list for {} may not be empty",
+                    input_derivation_path
+                );
+            }
+
+            for (i, output_name) in output_names.iter().enumerate() {
+                if output_name.is_empty() {
+                    bail!(
+                        "output name entry for {} may not be empty",
+                        input_derivation_path
+                    )
+                }
+                // if i is at least 1, peek at the previous element to ensure output_names are sorted.
+                if i > 0 && (output_names[i - 1] >= *output_name) {
+                    bail!(
+                        "invalid input derivation output order: {} < {}",
+                        output_name,
+                        output_names[i - 1],
+                    );
+                }
+            }
+        }
+
+        // Validate all input_sources
+        for (i, input_source) in self.input_sources.iter().enumerate() {
+            NixPath::from_absolute_path(input_source)?;
+
+            if i > 0 && self.input_sources[i - 1] >= *input_source {
+                bail!(
+                    "invalid input source order: {} < {}",
+                    input_source,
+                    self.input_sources[i - 1],
+                );
+            }
+        }
+
+        // validate platform
+        if self.system.is_empty() {
+            bail!("required attribute 'platform' missing");
+        }
+
+        // validate builder
+        if self.builder.is_empty() {
+            bail!("required attribute 'builder' missing");
+        }
+
+        // validate env, none of the keys may be empty.
+        // We skip the `name` validation seen in go-nix.
+        for k in self.environment.keys() {
+            if k.is_empty() {
+                bail!("found empty environment variable key");
+            }
+        }
+
+        Ok(())
+    }
+}