about summary refs log tree commit diff
path: root/tvix/derivation/src/validate.rs
blob: 5335f43636d3d436a541782a54f1e6c970f26812 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
use crate::{derivation::Derivation, write::DOT_FILE_EXT};
use anyhow::bail;
use tvix_store::store_path::StorePath;

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
            StorePath::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 output_name in output_names.iter() {
                if output_name.is_empty() {
                    bail!(
                        "output name entry for {} may not be empty",
                        input_derivation_path
                    )
                }
            }
        }

        // Validate all input_sources
        for (i, input_source) in self.input_sources.iter().enumerate() {
            StorePath::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(())
    }
}