Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add custom registry support #1048

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
293 changes: 293 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ regex = "1.11.1"
insta-cmd = "0.6.0"
rayon = "1.10.0"
trustfall_core = "0.8.0" # Ensure this matches the `trustfall` version above.
axum = "0.7.9"
tokio = "1.42.0"
tokio-util = "0.7.13"
tower = "0.5.1"
tower-http = { version = "0.6.1", features = ["fs", "timeout"] }
reqwest = "0.12.9"

# In dev and test profiles, compile all dependencies with optimizations enabled,
# but still checking debug assertions and overflows.
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,8 @@ The following flags can be used to explicitly specify a baseline instead:
The rustdoc json file to use as a semver baseline
```

Custom registries are not currently supported
([#160](https://github.com/obi1kenobi/cargo-semver-checks/issues/160)), so crates published on
registries other than crates.io should use one of the other approaches of generating the baseline.
For custom registries, use the `--registry` flag with the name of the registry
that you want to use as configured in your `config.toml`.

### What features does `cargo-semver-checks` enable in the tested crates?

Expand Down
16 changes: 16 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ pub struct GlobalConfig {
stdout: AutoStream<Box<dyn Write + 'static>>,
stderr: AutoStream<Box<dyn Write + 'static>>,
feature_flags: HashSet<FeatureFlag>,
/// Registry name to look up crates in
registry: Option<String>,
}

impl Default for GlobalConfig {
Expand All @@ -41,6 +43,7 @@ impl GlobalConfig {
stdout: AutoStream::new(Box::new(std::io::stdout()), stdout_choice),
stderr: AutoStream::new(Box::new(std::io::stderr()), stderr_choice),
feature_flags: HashSet::new(),
registry: None,
}
}

Expand Down Expand Up @@ -316,6 +319,19 @@ impl GlobalConfig {
pub fn feature_flags(&self) -> &HashSet<FeatureFlag> {
&self.feature_flags
}

/// Set (overwrite) the name of the registry to use for crate lookup
#[inline]
pub fn set_registry(&mut self, registry: String) -> &mut Self {
self.registry = Some(registry);
self
}

/// Return the name of the registry to use for crate lookup
#[inline]
pub fn registry(&self) -> Option<&str> {
self.registry.as_deref()
}
}

/// A feature flag for gating unstable `cargo-semver-checks` features.
Expand Down
3 changes: 3 additions & 0 deletions src/data_generation/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,9 @@ fn create_placeholder_rustdoc_manifest(
// Fixes: https://github.com/obi1kenobi/cargo-semver-checks/issues/261
version: Some(format!("={}", request.kind.version()?)),
default_features: request.default_features,
registry_index: Some(
request.kind.index_url().map(ToString::to_string)?.clone(),
),
features: request
.extra_features
.iter()
Expand Down
22 changes: 19 additions & 3 deletions src/data_generation/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use super::progress::{CallbackHandler, ProgressCallbacks};
#[derive(Debug, Clone)]
pub(super) struct RegistryRequest<'a> {
index_entry: &'a tame_index::IndexVersion,
// /// The url of the registry index that holds the specified crate
index_url: String,
}

#[derive(Debug, Clone)]
Expand All @@ -31,7 +33,7 @@ pub(super) enum RequestKind<'a> {
impl RequestKind<'_> {
pub(super) fn name(&self) -> anyhow::Result<&str> {
Ok(match self {
Self::Registry(RegistryRequest { index_entry }) => &index_entry.name,
Self::Registry(RegistryRequest { index_entry, .. }) => &index_entry.name,
Self::LocalProject(ProjectRequest { manifest }) => {
crate::manifest::get_package_name(manifest)?
}
Expand All @@ -40,12 +42,22 @@ impl RequestKind<'_> {

pub(super) fn version(&self) -> anyhow::Result<&str> {
Ok(match self {
Self::Registry(RegistryRequest { index_entry }) => index_entry.version.as_str(),
Self::Registry(RegistryRequest { index_entry, .. }) => index_entry.version.as_str(),
Self::LocalProject(ProjectRequest { manifest }) => {
crate::manifest::get_package_version(manifest)?
}
})
}

pub(super) fn index_url(&self) -> anyhow::Result<&str> {
Ok(match self {
Self::Registry(RegistryRequest { index_url, .. }) => index_url.as_str(),
Self::LocalProject(ProjectRequest { manifest: _ }) => {
// Unclear what we should do here, panic for now...
todo!()
Comment on lines +56 to +57
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@drewkett @obi1kenobi I wasn't sure what to do here, thoughts?

}
})
}
}

#[allow(dead_code)]
Expand Down Expand Up @@ -213,14 +225,18 @@ pub(crate) struct CrateDataRequest<'a> {
impl<'a> CrateDataRequest<'a> {
pub(crate) fn from_index(
index_entry: &'a tame_index::IndexVersion,
index_url: String,
default_features: bool,
extra_features: BTreeSet<Cow<'a, str>>,
build_target: Option<&'a str>,
is_baseline: bool,
) -> Self {
let features_fingerprint = make_features_hash(default_features, &extra_features);
Self {
kind: RequestKind::Registry(RegistryRequest { index_entry }),
kind: RequestKind::Registry(RegistryRequest {
index_entry,
index_url,
}),
default_features,
extra_features,
build_target,
Expand Down
9 changes: 9 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ fn main() {
None => args.check_release,
};

if let Some(registry) = &check_release.registry {
config.set_registry(registry.clone());
}

let check: cargo_semver_checks::Check = check_release.into();

let report = exit_on_error(config.is_error(), || check.check_release(&mut config));
Expand Down Expand Up @@ -532,6 +536,11 @@ struct CheckRelease {
#[arg(long = "target")]
build_target: Option<String>,

/// Name of registry to use for crate lookups. Used with default behavior
/// and with `--baseline-version`.
#[arg(long = "registry")]
registry: Option<String>,

#[clap(flatten)]
unstable_options: UnstableOptions,
}
Expand Down
77 changes: 52 additions & 25 deletions src/rustdoc_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ use crate::GlobalConfig;
pub(crate) enum CrateSource<'a> {
Registry {
crate_: &'a tame_index::IndexVersion,
/// The url of the registry index that holds the specified crate
index_url: String,
},
ManifestPath {
manifest: &'a Manifest,
Expand Down Expand Up @@ -265,8 +267,11 @@ fn generate_rustdoc(
);

let request = match crate_source {
CrateSource::Registry { crate_, .. } => CrateDataRequest::from_index(
CrateSource::Registry {
crate_, index_url, ..
} => CrateDataRequest::from_index(
crate_,
index_url,
default_features,
extra_features,
crate_data.build_target,
Expand Down Expand Up @@ -548,6 +553,10 @@ pub(crate) struct RustdocFromRegistry {
target_root: PathBuf,
version: Option<semver::Version>,
index: tame_index::index::ComboIndex,
/// The url of the index for the given registry
///
/// TODO unused!
index_url: String,
}

impl core::fmt::Debug for RustdocFromRegistry {
Expand All @@ -562,32 +571,46 @@ impl core::fmt::Debug for RustdocFromRegistry {

impl RustdocFromRegistry {
pub fn new(target_root: &std::path::Path, config: &mut GlobalConfig) -> anyhow::Result<Self> {
let index_url = tame_index::IndexUrl::crates_io(
// This is the config root, where .cargo/config.toml configuration files
// are crawled to determine if crates.io has been source replaced
// <https://doc.rust-lang.org/cargo/reference/source-replacement.html>
// if not specified it defaults to the current working directory,
// which is the same default that cargo uses, though note this can be
// extremely confusing if one can specify the manifest path of the
// crate from a different current working directory, though AFAICT
// this is not how this binary works
None,
// If set this overrides the CARGO_HOME that is used for both finding
// the "global" default config if not overriden during directory
// traversal to the root, as well as where the various registry
// indices/git sources are rooted. This is generally only useful
// for testing
None,
// If set, overrides the version of the cargo binary used, this is used
// as a fallback to determine if the version is 1.70.0+, which means
// the default crates.io registry to use is the sparse registry, else
// it is the old git registry
None,
)
.context("failed to obtain crates.io url")?;
let index_url = match config.registry() {
Some(registry_name) => {
tame_index::IndexUrl::for_registry_name(
// No need to override the config root. See comment below for more information.
None,
// No need to override the cargo home. See comment below for more information.
None,
registry_name,
)
.with_context(|| format!("failed to obtain url for registry '{}'", registry_name))?
}
None => tame_index::IndexUrl::crates_io(
// This is the config root, where .cargo/config.toml configuration files
// are crawled to determine if crates.io has been source replaced
// <https://doc.rust-lang.org/cargo/reference/source-replacement.html>
// if not specified it defaults to the current working directory,
// which is the same default that cargo uses, though note this can be
// extremely confusing if one can specify the manifest path of the
// crate from a different current working directory, though AFAICT
// this is not how this binary works
None,
// If set this overrides the CARGO_HOME that is used for both finding
// the "global" default config if not overriden during directory
// traversal to the root, as well as where the various registry
// indices/git sources are rooted. This is generally only useful
// for testing
None,
// If set, overrides the version of the cargo binary used, this is used
// as a fallback to determine if the version is 1.70.0+, which means
// the default crates.io registry to use is the sparse registry, else
// it is the old git registry
None,
)
.context("failed to obtain crates.io url")?,
};

use tame_index::index::{self, ComboIndexCache};

let index_url_str = index_url.as_str().to_string();

let index_cache = ComboIndexCache::new(tame_index::IndexLocation::new(index_url))
.context("failed to open crates.io index cache")?;

Expand Down Expand Up @@ -620,6 +643,7 @@ impl RustdocFromRegistry {
target_root: target_root.to_owned(),
version: None,
index,
index_url: index_url_str,
})
}

Expand Down Expand Up @@ -734,7 +758,10 @@ impl RustdocGenerator for RustdocFromRegistry {
generation_settings,
cache_settings,
self.target_root.clone(),
CrateSource::Registry { crate_ },
CrateSource::Registry {
index_url: self.index_url.clone(),
crate_,
},
crate_data,
)
}
Expand Down
2 changes: 2 additions & 0 deletions test_crates/custom_registry/new/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[registries]
custom-registry = { index = "sparse+http://127.0.0.1:5000/" }
2 changes: 2 additions & 0 deletions test_crates/custom_registry/new/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# needed for this test
!.cargo/config.toml
7 changes: 7 additions & 0 deletions test_crates/custom_registry/new/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
publish = false
name = "custom_registry"
version = "0.1.1"
edition = "2021"

[dependencies]
2 changes: 2 additions & 0 deletions test_crates/custom_registry/new/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub struct Dummy;
pub struct DummyNew;
2 changes: 2 additions & 0 deletions test_crates/custom_registry/old/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[registries]
custom-registry = { index = "sparse+http://127.0.0.1:5000/" }
2 changes: 2 additions & 0 deletions test_crates/custom_registry/old/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# needed for this test
!.cargo/config.toml
7 changes: 7 additions & 0 deletions test_crates/custom_registry/old/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
publish = false
name = "custom_registry"
version = "0.1.0"
edition = "2021"

[dependencies]
1 change: 1 addition & 0 deletions test_crates/custom_registry/old/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub struct Dummy;
1 change: 1 addition & 0 deletions test_crates/custom_registry/registry/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"dl":"http://127.0.0.1:5000/crates/{lowerprefix}/{crate}/{version}.crate","api":null,"auth-required":false}
Binary file not shown.
1 change: 1 addition & 0 deletions test_crates/custom_registry/registry/cu/st/custom_registry
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name":"custom_registry","vers":"0.1.0","deps":[],"cksum":"d0ad6b64ea3140e5d859432285042abe32bf3ec332b0d7d8869074bc9c159d7f","features":{},"yanked":false,"v":2}
7 changes: 7 additions & 0 deletions test_crates/custom_registry/registry/margo-config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version = "1"
base_url = "http://127.0.0.1:5000/"
auth_required = false

[html]
enabled = false
suggested_registry_name = "custom-registry"
67 changes: 67 additions & 0 deletions tests/custom_registry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use assert_cmd::Command;
use axum::Router;
use std::net::SocketAddr;
use std::str::FromStr;
use std::time::Duration;
use tokio_util::sync::CancellationToken;
use tower_http::services::ServeDir;
use tower_http::timeout::TimeoutLayer;

const HOST: &str = "127.0.0.1";
const PORT: u16 = 5000;

#[tokio::test]
async fn test_custom_registry() {
let token = CancellationToken::new();
let cloned_token = token.clone();
tokio::join!(
async {
tokio::time::timeout(Duration::from_secs(10), async {
while reqwest::get(format!("http://{HOST}:{PORT}")).await.is_err() {
tokio::time::sleep(Duration::from_millis(100)).await;
}
tokio::task::spawn_blocking(move || verify_custom_registry(token))
.await
.expect("registry failure");
})
.await
.expect("timeout failure");
},
serve(using_serve_dir(), cloned_token)
);
}

async fn serve(app: Router, token: CancellationToken) {
let addr = SocketAddr::from_str(&format!("{HOST}:{PORT}")).expect("Failed to parse HOST:PORT");
let listener = tokio::net::TcpListener::bind(addr)
.await
.expect("failed to bind");
axum::serve(
listener,
app.layer(TimeoutLayer::new(Duration::from_secs(10))),
)
.with_graceful_shutdown(shutdown_signal(token))
.await
.expect("failed to serve");
}

fn using_serve_dir() -> Router {
Router::new().nest_service("/", ServeDir::new("test_crates/custom_registry/registry"))
}

fn verify_custom_registry(token: CancellationToken) {
let mut cmd =
Command::cargo_bin("cargo-semver-checks").expect("failed to execute cargo-semver-checks");
let result = cmd
.current_dir("test_crates/custom_registry/new")
.args(["semver-checks", "--registry", "custom-registry"])
.assert();
token.cancel();
result.success();
}

async fn shutdown_signal(token: CancellationToken) {
tokio::select! {
_ = token.cancelled() => {}
}
}
Loading
Loading