From a6a69cb68f1b9f4c9dd8aab0ecd55ee86218682c Mon Sep 17 00:00:00 2001 From: Ryan Bottriell Date: Tue, 20 Dec 2022 14:06:19 -0800 Subject: [PATCH] Add check to disable publishing unstable APIs Signed-off-by: Ryan Bottriell --- crates/spk-cli/common/src/publish.rs | 20 ++++++++ crates/spk-cli/group2/src/cmd_publish.rs | 7 +++ crates/spk-schema/src/spec.rs | 60 +++++++++++++++++------- 3 files changed, 70 insertions(+), 17 deletions(-) diff --git a/crates/spk-cli/common/src/publish.rs b/crates/spk-cli/common/src/publish.rs index 3dd7342d4e..9f372af7f1 100644 --- a/crates/spk-cli/common/src/publish.rs +++ b/crates/spk-cli/common/src/publish.rs @@ -26,6 +26,7 @@ pub struct Publisher { from: Arc, to: Arc, skip_source_packages: bool, + allow_unstable_api: bool, force: bool, } @@ -42,6 +43,7 @@ impl Publisher { from: source, to: destination, skip_source_packages: false, + allow_unstable_api: false, force: false, } } @@ -64,6 +66,12 @@ impl Publisher { self } + /// Allow publishing packages defined with an unstable api version + pub fn allow_unstable_api(mut self, allow_unstable_api: bool) -> Self { + self.allow_unstable_api = allow_unstable_api; + self + } + /// Forcefully publishing a package will overwrite an existing publish if it exists. pub fn force(mut self, force: bool) -> Self { self.force = force; @@ -93,6 +101,12 @@ impl Publisher { } Err(err) => return Err(err.into()), Ok(recipe) => { + if !recipe.api_version().is_stable() && !self.allow_unstable_api { + return Err(Error::String(format!( + "Recipe has unstable api version, and allow unstable is not set: {:?}", + recipe.api_version() + ))); + } tracing::info!("publishing recipe: {}", recipe.ident().format_ident()); if self.force { self.to.force_publish_recipe(&recipe).await?; @@ -151,6 +165,12 @@ impl Publisher { tracing::debug!(" loading package: {}", build.format_ident()); let spec = self.from.read_package(build).await?; + if !spec.api_version().is_stable() && !self.allow_unstable_api { + return Err(Error::String(format!( + "Package build has unstable api version, and allow unstable is not set: {:?}", + spec.api_version() + ))); + } let components = self.from.read_components(build).await?; tracing::info!("publishing package: {}", spec.ident().format_ident()); let env_spec = components.values().cloned().collect(); diff --git a/crates/spk-cli/group2/src/cmd_publish.rs b/crates/spk-cli/group2/src/cmd_publish.rs index 43f73949a9..4b1524f64d 100644 --- a/crates/spk-cli/group2/src/cmd_publish.rs +++ b/crates/spk-cli/group2/src/cmd_publish.rs @@ -34,6 +34,12 @@ pub struct Publish { #[clap(long, short)] force: bool, + /// Allow publishing package specs written in unstable api versions + /// + /// The api version of a spec is the value of the `api` field within it. + #[clap(long)] + allow_unstable_api: bool, + /// The local packages to publish /// /// This can be an entire package version with all builds or a @@ -54,6 +60,7 @@ impl Run for Publish { let publisher = Publisher::new(Arc::new(source.into()), Arc::new(target.into())) .skip_source_packages(self.no_source) + .allow_unstable_api(self.allow_unstable_api) .force(self.force); let mut published = Vec::new(); diff --git a/crates/spk-schema/src/spec.rs b/crates/spk-schema/src/spec.rs index e585c4050e..63a406a3e6 100644 --- a/crates/spk-schema/src/spec.rs +++ b/crates/spk-schema/src/spec.rs @@ -246,6 +246,15 @@ pub enum SpecRecipe { V0Platform(super::v0::Platform), } +impl SpecRecipe { + pub fn api_version(&self) -> RecipeApiVersion { + match self { + Self::V0Package(_) => RecipeApiVersion::V0Package, + Self::V0Platform(_) => RecipeApiVersion::V0Platform, + } + } +} + impl Recipe for SpecRecipe { type Output = Spec; type Variant = SpecVariant; @@ -398,8 +407,8 @@ impl FromYaml for SpecRecipe { // fairly generic, eg: 'expected struct YamlMapping' #[derive(Deserialize)] struct YamlMapping { - #[serde(default = "ApiVersion::default")] - api: ApiVersion, + #[serde(default = "RecipeApiVersion::default")] + api: RecipeApiVersion, } let with_version = match serde_yaml::from_str::(&yaml) { @@ -413,12 +422,12 @@ impl FromYaml for SpecRecipe { }; match with_version.api { - ApiVersion::V0Package => { + RecipeApiVersion::V0Package => { let inner = serde_yaml::from_str(&yaml) .map_err(|err| SerdeError::new(yaml, SerdeYamlError(err)))?; Ok(Self::V0Package(inner)) } - ApiVersion::V0Platform => { + RecipeApiVersion::V0Platform => { let inner = serde_yaml::from_str(&yaml) .map_err(|err| SerdeError::new(yaml, SerdeYamlError(err)))?; Ok(Self::V0Platform(inner)) @@ -490,6 +499,14 @@ pub enum Spec { V0Package(super::v0::Spec), } +impl Spec { + pub fn api_version(&self) -> PackageApiVersion { + match self { + Self::V0Package(_) => PackageApiVersion::V0Package, + } + } +} + impl Satisfy for Spec { fn check_satisfies_request(&self, request: &PkgRequest) -> Compatibility { match self { @@ -659,8 +676,8 @@ impl FromYaml for Spec { // fairly generic, eg: 'expected struct YamlMapping' #[derive(Deserialize)] struct YamlMapping { - #[serde(default = "ApiVersion::default")] - api: ApiVersion, + #[serde(default = "PackageApiVersion::default")] + api: PackageApiVersion, } let with_version = match serde_yaml::from_str::(&yaml) { @@ -674,12 +691,7 @@ impl FromYaml for Spec { }; match with_version.api { - ApiVersion::V0Package => { - let inner = serde_yaml::from_str(&yaml) - .map_err(|err| SerdeError::new(yaml, SerdeYamlError(err)))?; - Ok(Self::V0Package(inner)) - } - ApiVersion::V0Platform => { + PackageApiVersion::V0Package => { let inner = serde_yaml::from_str(&yaml) .map_err(|err| SerdeError::new(yaml, SerdeYamlError(err)))?; Ok(Self::V0Package(inner)) @@ -694,16 +706,30 @@ impl AsRef for Spec { } } -#[derive(Deserialize, Serialize, Copy, Clone)] -pub enum ApiVersion { +#[derive(Default, Debug, Deserialize, Serialize, Copy, Clone)] +pub enum RecipeApiVersion { + #[default] #[serde(rename = "v0/package")] V0Package, #[serde(rename = "v0/platform")] V0Platform, } -impl Default for ApiVersion { - fn default() -> Self { - Self::V0Package +impl RecipeApiVersion { + pub fn is_stable(&self) -> bool { + matches!(self, Self::V0Package) + } +} + +#[derive(Default, Debug, Deserialize, Serialize, Copy, Clone)] +pub enum PackageApiVersion { + #[default] + #[serde(rename = "v0/package")] + V0Package, +} + +impl PackageApiVersion { + pub fn is_stable(&self) -> bool { + matches!(self, Self::V0Package) } }