diff --git a/docs/src/content/docs/guides/self-hosting-registries.mdx b/docs/src/content/docs/guides/self-hosting-registries.mdx index 82dfa38..f51ffcc 100644 --- a/docs/src/content/docs/guides/self-hosting-registries.mdx +++ b/docs/src/content/docs/guides/self-hosting-registries.mdx @@ -19,10 +19,10 @@ To create an index, create a new repository and add a `config.toml` file with the following content: ```toml title="config.toml" -# The URL of the registry API +# the URL of the registry API api = "https://registry.acme.local/" -# Package download URL (optional) +# package download URL (optional) download = "{API_URL}/v0/packages/{PACKAGE}/{PACKAGE_VERSION}/{PACKAGE_TARGET}" # the client ID of the GitHub OAuth app (optional) @@ -33,13 +33,16 @@ git_allowed = true # whether to allow packages which depend on packages from other registries # (default: false) -other_registries_allowed = true +other_registries_allowed = ["https://git.acme.local/index"] # whether to allow packages with Wally dependencies (default: false) wally_allowed = false # the maximum size of the archive in bytes (default: 4MB) max_archive_size = 4194304 + +# the scripts packages present in the `init` command selection by default +scripts_packages = ["pesde/scripts_rojo"] ``` - **api**: The URL of the registry API. See below for more information. @@ -60,18 +63,24 @@ max_archive_size = 4194304 - **github_oauth_client_id**: This is required if you use GitHub OAuth for authentication. See below for more information. -- **git_allowed**: Whether to allow packages with Git dependencies. This is - optional and defaults to `false`. +- **git_allowed**: Whether to allow packages with Git dependencies. This can be + either a bool or a list of allowed repository URLs. This is optional and + defaults to `false`. - **other_registries_allowed**: Whether to allow packages which depend on - packages from other registries. This is optional and defaults to `false`. + packages from other registries. This can be either a bool or a list of + allowed index repository URLs. This is optional and defaults to `false`. -- **wally_allowed**: Whether to allow packages with Wally dependencies. This is +- **wally_allowed**: Whether to allow packages with Wally dependencies. This can + be either a bool or a list of allowed index repository URLs. This is optional and defaults to `false`. - **max_archive_size**: The maximum size of the archive in bytes. This is optional and defaults to `4194304` (4MB). +- **scripts_packages**: The scripts packages present in the `init` command + selection by default. This is optional and defaults to none. + You should then push this repository to [GitHub](https://github.com/). ## Configuring the registry @@ -88,8 +97,8 @@ has access to the index repository. We recommend using a separate account for this purpose. diff --git a/registry/src/endpoints/publish_version.rs b/registry/src/endpoints/publish_version.rs index 929ddbf..42b21e2 100644 --- a/registry/src/endpoints/publish_version.rs +++ b/registry/src/endpoints/publish_version.rs @@ -304,7 +304,7 @@ pub async fn publish_package( .filter(|index| match gix::Url::try_from(*index) { Ok(url) => config .other_registries_allowed - .is_allowed(source.repo_url().clone(), url), + .is_allowed_or_same(source.repo_url().clone(), url), Err(_) => false, }) .is_none() @@ -315,16 +315,13 @@ pub async fn publish_package( } } DependencySpecifiers::Wally(specifier) => { - if !config.wally_allowed { - return Err(Error::InvalidArchive( - "wally dependencies are not allowed".into(), - )); - } - if specifier .index - .as_ref() - .filter(|index| index.parse::().is_ok()) + .as_deref() + .filter(|index| match gix::Url::try_from(*index) { + Ok(url) => config.wally_allowed.is_allowed(url), + Err(_) => false, + }) .is_none() { return Err(Error::InvalidArchive(format!( @@ -332,15 +329,15 @@ pub async fn publish_package( ))); } } - DependencySpecifiers::Git(_) => { - if !config.git_allowed { + DependencySpecifiers::Git(specifier) => { + if !config.git_allowed.is_allowed(specifier.repo.clone()) { return Err(Error::InvalidArchive( "git dependencies are not allowed".into(), )); } } DependencySpecifiers::Workspace(_) => { - // workspace specifiers are to be transformed into Pesde specifiers by the sender + // workspace specifiers are to be transformed into pesde specifiers by the sender return Err(Error::InvalidArchive( "non-transformed workspace dependency".into(), )); diff --git a/src/cli/commands/publish.rs b/src/cli/commands/publish.rs index 855643b..550b1a8 100644 --- a/src/cli/commands/publish.rs +++ b/src/cli/commands/publish.rs @@ -9,6 +9,7 @@ use pesde::{ matching_globs_old_behaviour, scripts::ScriptName, source::{ + git_index::GitBasedSource, pesde::{specifier::PesdeDependencySpecifier, PesdePackageSource}, specifiers::DependencySpecifiers, traits::PackageSource, @@ -362,10 +363,6 @@ info: otherwise, the file was deemed unnecessary, if you don't understand why, p } } - #[cfg(feature = "wally-compat")] - let mut has_wally = false; - let mut has_git = false; - for specifier in manifest .dependencies .values_mut() @@ -389,8 +386,6 @@ info: otherwise, the file was deemed unnecessary, if you don't understand why, p } #[cfg(feature = "wally-compat")] DependencySpecifiers::Wally(specifier) => { - has_wally = true; - let index_name = specifier .index .as_deref() @@ -406,9 +401,7 @@ info: otherwise, the file was deemed unnecessary, if you don't understand why, p .to_string(), ); } - DependencySpecifiers::Git(_) => { - has_git = true; - } + DependencySpecifiers::Git(_) => {} DependencySpecifiers::Workspace(spec) => { let pkg_ref = WorkspacePackageSource .resolve(spec, project, target_kind, &mut HashSet::new()) @@ -570,8 +563,7 @@ info: otherwise, the file was deemed unnecessary, if you don't understand why, p .get(&self.index) .context(format!("missing index {}", self.index))?; let source = PesdePackageSource::new(index_url.clone()); - source - .refresh(project) + PackageSource::refresh(&source, project) .await .context("failed to refresh source")?; let config = source @@ -587,15 +579,31 @@ info: otherwise, the file was deemed unnecessary, if you don't understand why, p ); } - manifest.all_dependencies().context("dependency conflict")?; + let deps = manifest.all_dependencies().context("dependency conflict")?; - if !config.git_allowed && has_git { - anyhow::bail!("git dependencies are not allowed on this index"); - } - - #[cfg(feature = "wally-compat")] - if !config.wally_allowed && has_wally { - anyhow::bail!("wally dependencies are not allowed on this index"); + if let Some((disallowed, _)) = deps.iter().find(|(_, (spec, _))| match spec { + DependencySpecifiers::Pesde(spec) => { + !config.other_registries_allowed.is_allowed_or_same( + source.repo_url().clone(), + manifest + .indices + .get(spec.index.as_deref().unwrap_or(DEFAULT_INDEX_NAME)) + .unwrap() + .clone(), + ) + } + DependencySpecifiers::Git(spec) => !config.git_allowed.is_allowed(spec.repo.clone()), + #[cfg(feature = "wally-compat")] + DependencySpecifiers::Wally(spec) => !config.wally_allowed.is_allowed( + manifest + .wally_indices + .get(spec.index.as_deref().unwrap_or(DEFAULT_INDEX_NAME)) + .unwrap() + .clone(), + ), + _ => false, + }) { + anyhow::bail!("dependency `{disallowed}` is not allowed on this index"); } if self.dry_run { diff --git a/src/source/pesde/mod.rs b/src/source/pesde/mod.rs index 45816ae..a39037c 100644 --- a/src/source/pesde/mod.rs +++ b/src/source/pesde/mod.rs @@ -274,22 +274,30 @@ impl Default for AllowedRegistries { } } -impl AllowedRegistries { - /// Whether the given URL is allowed - pub fn is_allowed(&self, mut this: Url, mut external: Url) -> bool { - // strip .git suffix to allow for more flexible matching - this.path = this.path.strip_suffix(b".git").unwrap_or(&this.path).into(); - external.path = external - .path - .strip_suffix(b".git") - .unwrap_or(&external.path) - .into(); +// strips .git suffix to allow for more flexible matching +fn simplify_url(mut url: Url) -> Url { + url.path = url.path.strip_suffix(b".git").unwrap_or(&url.path).into(); + url +} - this == external - || (match self { - Self::All(all) => *all, - Self::Specific(urls) => urls.contains(&this) || urls.contains(&external), - }) +impl AllowedRegistries { + fn _is_allowed(&self, url: &Url) -> bool { + match self { + Self::All(all) => *all, + Self::Specific(urls) => urls.contains(url), + } + } + + /// Whether the given URL is allowed + pub fn is_allowed(&self, url: Url) -> bool { + self._is_allowed(&simplify_url(url)) + } + + /// Whether the given URL is allowed, or is the same as the given URL + pub fn is_allowed_or_same(&self, this: Url, external: Url) -> bool { + let this = simplify_url(this); + let external = simplify_url(external); + (this == external) || self._is_allowed(&external) || self._is_allowed(&this) } } @@ -302,13 +310,13 @@ pub struct IndexConfig { pub download: Option, /// Whether Git is allowed as a source for publishing packages #[serde(default)] - pub git_allowed: bool, + pub git_allowed: AllowedRegistries, /// Whether other registries are allowed as a source for publishing packages #[serde(default)] pub other_registries_allowed: AllowedRegistries, /// Whether Wally is allowed as a source for publishing packages #[serde(default)] - pub wally_allowed: bool, + pub wally_allowed: AllowedRegistries, /// The OAuth client ID for GitHub #[serde(default)] pub github_oauth_client_id: Option,