From b69dd75ec80c6a94bd0bba1164b3596952801bb1 Mon Sep 17 00:00:00 2001 From: Wolf Vollprecht Date: Fri, 14 Jul 2023 14:31:13 +0200 Subject: [PATCH] feat: added some metadata fields inspired by cargo & conda-build (#218) --- Cargo.lock | 10 ++++ Cargo.toml | 1 + examples/flask-hello-world/README.md | 3 ++ examples/flask-hello-world/pixi.toml | 3 ++ src/project/manifest.rs | 52 ++++++++++++++++++- src/project/mod.rs | 9 ++-- ...t__manifest__test__activation_scripts.snap | 6 +++ ...ect__manifest__test__dependency_types.snap | 6 +++ ...ject__manifest__test__target_specific.snap | 6 +++ 9 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 examples/flask-hello-world/README.md diff --git a/Cargo.lock b/Cargo.lock index 76afd5604..040d15565 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2077,6 +2077,7 @@ dependencies = [ "serde_spanned", "serde_with", "shlex", + "spdx", "tempfile", "tokio", "tokio-util", @@ -2971,6 +2972,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "spdx" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2971cb691ca629f46174f73b1f95356c5617f89b4167f04107167c3dccb8dd89" +dependencies = [ + "smallvec", +] + [[package]] name = "spin" version = "0.5.2" diff --git a/Cargo.toml b/Cargo.toml index fcef0707b..6c82be288 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ serde_json = "1.0.96" serde_spanned = "0.6.2" serde_with = { version = "3.0.0", features = ["indexmap"] } shlex = "1.1.0" +spdx = "0.10.1" tempfile = "3.5.0" tokio = { version = "1.27.0", features = ["macros", "rt-multi-thread", "signal"] } tokio-util = "0.7.8" diff --git a/examples/flask-hello-world/README.md b/examples/flask-hello-world/README.md new file mode 100644 index 000000000..4e0cbd004 --- /dev/null +++ b/examples/flask-hello-world/README.md @@ -0,0 +1,3 @@ +# This is a very simple example of a flask project + +Just run `pixi run start` to get going with a flask server. diff --git a/examples/flask-hello-world/pixi.toml b/examples/flask-hello-world/pixi.toml index f395d6b3a..c235d34f0 100644 --- a/examples/flask-hello-world/pixi.toml +++ b/examples/flask-hello-world/pixi.toml @@ -5,6 +5,9 @@ description = "Example how to get started with flask in a pixi environment." authors = ["Wolf Vollprecht "] channels = ["conda-forge"] platforms = ["linux-64", "win-64", "osx-64", "osx-arm64"] +license = "MIT OR Apache-2.0" +homepage = "https://github.com/prefix/pixi" +readme = "README.md" [tasks] start = "python -m flask run --port=5050" diff --git a/src/project/manifest.rs b/src/project/manifest.rs index 7e09bcc20..e6d54b5e7 100644 --- a/src/project/manifest.rs +++ b/src/project/manifest.rs @@ -1,7 +1,7 @@ use crate::{consts, task::Task}; use ::serde::Deserialize; use indexmap::IndexMap; -use miette::{LabeledSpan, NamedSource, Report}; +use miette::{Context, IntoDiagnostic, LabeledSpan, NamedSource, Report}; use rattler_conda_types::{Channel, NamelessMatchSpec, Platform, Version}; use rattler_virtual_packages::{Archspec, Cuda, LibC, Linux, Osx, VirtualPackage}; use serde::Deserializer; @@ -10,6 +10,8 @@ use serde_with::de::DeserializeAsWrap; use serde_with::{serde_as, DeserializeAs, DisplayFromStr, PickFirst}; use std::collections::HashMap; use std::ops::Range; +use std::path::{Path, PathBuf}; +use url::Url; /// Describes the contents of a project manifest. #[serde_as] @@ -66,7 +68,7 @@ pub struct ProjectManifest { impl ProjectManifest { /// Validate the - pub fn validate(&self, source: NamedSource) -> miette::Result<()> { + pub fn validate(&self, source: NamedSource, root_folder: &Path) -> miette::Result<()> { // Check if the targets are defined for existing platforms for target_sel in self.target.keys() { match target_sel.as_ref() { @@ -82,6 +84,34 @@ impl ProjectManifest { } } + // parse the SPDX license expression to make sure that it is a valid expression. + if let Some(spdx_expr) = &self.project.license { + spdx::Expression::parse(spdx_expr) + .into_diagnostic() + .with_context(|| { + format!( + "failed to parse the SPDX license expression '{}'", + spdx_expr + ) + })?; + } + + let check_file_existence = |x: &Option| { + if let Some(path) = x { + let full_path = root_folder.join(path); + if !full_path.exists() { + return Err(miette::miette!( + "the file '{}' does not exist", + full_path.display() + )); + } + } + Ok(()) + }; + + check_file_existence(&self.project.license_file)?; + check_file_existence(&self.project.readme)?; + Ok(()) } } @@ -169,6 +199,24 @@ pub struct ProjectMetadata { // TODO: This is actually slightly different from the rattler_conda_types::Platform because it // should not include noarch. pub platforms: Spanned>, + + /// The license as a valid SPDX string (e.g. MIT AND Apache-2.0) + pub license: Option, + + /// The license file (relative to the project root) + pub license_file: Option, + + /// Path to the README file of the project (relative to the project root) + pub readme: Option, + + /// URL of the project homepage + pub homepage: Option, + + /// URL of the project source repository + pub repository: Option, + + /// URL of the project documentation + pub documentation: Option, } #[serde_as] diff --git a/src/project/mod.rs b/src/project/mod.rs index 1f5e9ce3e..d8ee21b99 100644 --- a/src/project/mod.rs +++ b/src/project/mod.rs @@ -63,6 +63,7 @@ fn task_as_toml(task: Task) -> Item { impl Project { /// Discovers the project manifest file in the current directory or any of the parent /// directories. + /// This will also set the current working directory to the project root. pub fn discover() -> miette::Result { let project_toml = match find_project_root() { Some(root) => root.join(consts::PROJECT_MANIFEST), @@ -212,10 +213,10 @@ impl Project { }; // Validate the contents of the manifest - manifest.validate(NamedSource::new( - consts::PROJECT_MANIFEST, - contents.to_owned(), - ))?; + manifest.validate( + NamedSource::new(consts::PROJECT_MANIFEST, contents.to_owned()), + root, + )?; Ok(Self { root: root.to_path_buf(), diff --git a/src/project/snapshots/pixi__project__manifest__test__activation_scripts.snap b/src/project/snapshots/pixi__project__manifest__test__activation_scripts.snap index a9248025f..da861f7ca 100644 --- a/src/project/snapshots/pixi__project__manifest__test__activation_scripts.snap +++ b/src/project/snapshots/pixi__project__manifest__test__activation_scripts.snap @@ -16,6 +16,12 @@ ProjectManifest { span: 121..123, value: [], }, + license: None, + license_file: None, + readme: None, + homepage: None, + repository: None, + documentation: None, }, tasks: {}, system_requirements: SystemRequirements { diff --git a/src/project/snapshots/pixi__project__manifest__test__dependency_types.snap b/src/project/snapshots/pixi__project__manifest__test__dependency_types.snap index 88c125325..1d32411e1 100644 --- a/src/project/snapshots/pixi__project__manifest__test__dependency_types.snap +++ b/src/project/snapshots/pixi__project__manifest__test__dependency_types.snap @@ -16,6 +16,12 @@ ProjectManifest { span: 121..123, value: [], }, + license: None, + license_file: None, + readme: None, + homepage: None, + repository: None, + documentation: None, }, tasks: {}, system_requirements: SystemRequirements { diff --git a/src/project/snapshots/pixi__project__manifest__test__target_specific.snap b/src/project/snapshots/pixi__project__manifest__test__target_specific.snap index 17cd18a35..5bbafdfed 100644 --- a/src/project/snapshots/pixi__project__manifest__test__target_specific.snap +++ b/src/project/snapshots/pixi__project__manifest__test__target_specific.snap @@ -16,6 +16,12 @@ ProjectManifest { span: 117..119, value: [], }, + license: None, + license_file: None, + readme: None, + homepage: None, + repository: None, + documentation: None, }, tasks: {}, system_requirements: SystemRequirements {