Skip to main content

xtask/
release_versions_manifest.rs

1use super::{Component, Manifest, VersionFile, VersionKind};
2use anyhow::{bail, Result};
3use std::collections::HashSet;
4
5pub(super) fn validate_manifest(manifest: &Manifest) -> Result<()> {
6    let mut component_ids = HashSet::new();
7    let mut tag_prefixes: Vec<&str> = Vec::new();
8    for component in &manifest.components {
9        if component.id.trim().is_empty() {
10            bail!("release manifest contains an empty component id");
11        }
12        if !component_ids.insert(component.id.as_str()) {
13            bail!("duplicate release component id {}", component.id);
14        }
15        if component.tag_prefix.trim().is_empty() {
16            bail!("{} has an empty tag_prefix", component.id);
17        }
18        if tag_prefixes.iter().any(|existing| {
19            existing.starts_with(&component.tag_prefix)
20                || component.tag_prefix.starts_with(*existing)
21        }) {
22            bail!("{} tag_prefix overlaps another component", component.id);
23        }
24        tag_prefixes.push(&component.tag_prefix);
25        if component.shipping_paths.is_empty() {
26            bail!("{} has no shipping_paths", component.id);
27        }
28        if !component.release_workflow.ends_with(".yml")
29            && !component.release_workflow.ends_with(".yaml")
30        {
31            bail!("{} release_workflow must be a YAML workflow", component.id);
32        }
33        validate_version_file(component, "version_source", &component.version_source)?;
34        for file in &component.version_files {
35            validate_version_file(component, "version_files", file)?;
36        }
37        if !component
38            .version_files
39            .iter()
40            .any(|file| same_version_file(file, &component.version_source))
41        {
42            bail!(
43                "{} version_source is not listed in version_files",
44                component.id
45            );
46        }
47    }
48    Ok(())
49}
50
51fn validate_version_file(component: &Component, field: &str, file: &VersionFile) -> Result<()> {
52    match file.kind {
53        VersionKind::CargoPackage | VersionKind::CargoLockPackage => {
54            if file.package.as_deref().unwrap_or("").trim().is_empty() {
55                bail!(
56                    "{} {field} {} {:?} requires package",
57                    component.id,
58                    file.path,
59                    file.kind
60                );
61            }
62            if file.json_pointer.is_some() {
63                bail!(
64                    "{} {field} {} {:?} must not set json_pointer",
65                    component.id,
66                    file.path,
67                    file.kind
68                );
69            }
70        }
71        VersionKind::JsonVersion
72        | VersionKind::OciIdentifierVersion
73        | VersionKind::NpmIdentifierVersion => {
74            if file.package.is_some() {
75                bail!(
76                    "{} {field} {} {:?} must not set package",
77                    component.id,
78                    file.path,
79                    file.kind
80                );
81            }
82            let pointer = file.json_pointer.as_deref().unwrap_or("");
83            if !pointer.starts_with('/') {
84                bail!(
85                    "{} {field} {} {:?} requires an absolute json_pointer",
86                    component.id,
87                    file.path,
88                    file.kind
89                );
90            }
91        }
92        VersionKind::ChangelogHeading | VersionKind::JsonNoVersion => {
93            if file.package.is_some() || file.json_pointer.is_some() {
94                bail!(
95                    "{} {field} {} {:?} must not set package/json_pointer",
96                    component.id,
97                    file.path,
98                    file.kind
99                );
100            }
101        }
102    }
103    Ok(())
104}
105
106fn same_version_file(left: &VersionFile, right: &VersionFile) -> bool {
107    left.kind == right.kind
108        && left.path == right.path
109        && left.package == right.package
110        && left.json_pointer == right.json_pointer
111}