Skip to content
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
11 changes: 11 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

[registries.csc-imaginary-registry]
credential-provider = ["cargo:token"]
index = "sparse+https://csc-imaginary-registry.example.net/index/"

[source.csc-imaginary-registry]
registry = "sparse+https://csc-imaginary-registry.example.net/index/"
replace-with = "csc-actual-registry-on-disk"

[source.csc-actual-registry-on-disk]
local-registry = "test_crates/custom_registry/test-local-registry"
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,7 @@ default = ["gix-reqwest"]
# enable:
gix-reqwest = ["tame-index/gix-reqwest"]
gix-curl = ["tame-index/gix-curl"]

# Support Cargo source replacement config which uses `local-registry` paths.
# This is a different feature than the standard crates.io registry cache in `CARGO_HOME`.
local-registry-replacement = ["tame-index/local"]
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,7 @@ 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.
Custom registries are detected based on `publish = ["registry-name"]` property in `Cargo.toml`.

#### Git repository detection and configuration

Expand Down
8 changes: 4 additions & 4 deletions src/data_generation/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl GenerationSettings {
}
}

fn color_flag(&self) -> &'static str {
pub(crate) fn color_flag(&self) -> &'static str {
if self.use_color {
"--color=always"
} else {
Expand All @@ -45,6 +45,7 @@ impl GenerationSettings {
pub(super) fn generate_rustdoc(
request: &CrateDataRequest<'_>,
build_dir: &Path,
target_dir: &Path,
settings: GenerationSettings,
callbacks: &mut CallbackHandler<'_>,
) -> Result<(PathBuf, cargo_metadata::Metadata), TerminalError> {
Expand Down Expand Up @@ -105,8 +106,6 @@ pub(super) fn generate_rustdoc(
let metadata = cargo_metadata::MetadataCommand::new()
.manifest_path(&placeholder_manifest_path)
.exec()?;
let placeholder_target_directory = metadata.target_directory.as_path().as_std_path().to_owned();
let target_dir = placeholder_target_directory.as_path();

let rustdoc_data = run_cargo_doc(
request,
Expand Down Expand Up @@ -536,11 +535,12 @@ fn create_placeholder_rustdoc_manifest(
},
dependencies: {
let project_with_features: DependencyDetail = match &request.kind {
RequestKind::Registry { .. } => DependencyDetail {
RequestKind::Registry(registry_request) => DependencyDetail {
// We need the *exact* version as a dependency, or else cargo will
// give us the latest semver-compatible version which is not we want.
// Fixes: https://github.com/obi1kenobi/cargo-semver-checks/issues/261
version: Some(format!("={}", request.kind.version()?)),
registry: registry_request.registry_name.map(From::from),
default_features: request.default_features,
features: request
.extra_features
Expand Down
16 changes: 13 additions & 3 deletions src/data_generation/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use super::progress::{CallbackHandler, ProgressCallbacks};
#[derive(Debug, Clone)]
pub(super) struct RegistryRequest<'a> {
index_entry: &'a tame_index::IndexVersion,
pub(super) registry_name: Option<&'a str>,
}

#[derive(Debug, Clone)]
Expand All @@ -31,7 +32,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,7 +41,7 @@ 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)?
}
Expand Down Expand Up @@ -213,14 +214,18 @@ pub(crate) struct CrateDataRequest<'a> {
impl<'a> CrateDataRequest<'a> {
pub(crate) fn from_index(
index_entry: &'a tame_index::IndexVersion,
registry_name: Option<&'a str>,
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,
registry_name,
}),
default_features,
extra_features,
build_target,
Expand Down Expand Up @@ -331,9 +336,14 @@ impl<'a> CrateDataRequest<'a> {

// Generate the data we need.
let build_dir = target_root.join(self.build_path_slug().into_terminal_result()?);
// To reduce redundant dependency rebuilds, share the same target between builds
// of crates from the same workspace
let target_dir = target_root.join("target");

let (data_path, metadata) = super::generate::generate_rustdoc(
self,
&build_dir,
&target_dir,
generation_settings,
&mut callbacks,
)?;
Expand Down
91 changes: 79 additions & 12 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ use directories::ProjectDirs;
use itertools::Itertools;
use serde::Serialize;

use std::collections::{BTreeMap, HashSet};
use std::collections::hash_map::Entry;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::io::Write as _;
use std::path::{Path, PathBuf};
use std::rc::Rc;

use check_release::run_check_release;
use rustdoc_gen::CrateDataForRustdoc;
use rustdoc_gen::{CrateDataForRustdoc, RustdocGenerator};

pub use config::{FeatureFlag, GlobalConfig};
pub use query::{
Expand Down Expand Up @@ -112,7 +114,7 @@ impl Rustdoc {
}
}

#[derive(Debug, PartialEq, Eq, Serialize)]
#[derive(Debug, Hash, PartialEq, Eq, Serialize)]
enum RustdocSource {
/// Path to the Rustdoc json file.
/// Use this option when you have already generated the rustdoc file.
Expand Down Expand Up @@ -371,32 +373,37 @@ impl Check {
&self,
config: &mut GlobalConfig,
source: &RustdocSource,
) -> anyhow::Result<Box<dyn rustdoc_gen::RustdocGenerator>> {
custom_registry_name: Option<&str>,
) -> anyhow::Result<Rc<dyn rustdoc_gen::RustdocGenerator>> {
let target_dir = self.get_target_dir(source)?;
Ok(match source {
RustdocSource::Rustdoc(path) => {
Box::new(rustdoc_gen::RustdocFromFile::new(path.to_owned()))
Rc::new(rustdoc_gen::RustdocFromFile::new(path.to_owned()))
}
RustdocSource::Root(root) => {
Box::new(rustdoc_gen::RustdocFromProjectRoot::new(root, &target_dir)?)
Rc::new(rustdoc_gen::RustdocFromProjectRoot::new(root, &target_dir)?)
}
RustdocSource::Revision(root, rev) => {
let metadata = manifest_metadata_no_deps(root)?;
let source = metadata.workspace_root.as_std_path();
Box::new(rustdoc_gen::RustdocFromGitRevision::with_rev(
Rc::new(rustdoc_gen::RustdocFromGitRevision::with_rev(
source,
&target_dir,
rev,
config,
)?)
}
RustdocSource::VersionFromRegistry(version) => {
let mut registry = rustdoc_gen::RustdocFromRegistry::new(&target_dir, config)?;
let mut registry = rustdoc_gen::RustdocFromRegistry::new(
&target_dir,
custom_registry_name,
config,
)?;
if let Some(ver) = version {
let semver = semver::Version::parse(ver)?;
registry.set_version(semver);
}
Box::new(registry)
Rc::new(registry)
}
})
}
Expand Down Expand Up @@ -461,6 +468,7 @@ impl Check {
current_crate_data: CrateDataForRustdoc {
crate_type: rustdoc_gen::CrateType::Current,
name: name.clone(),
registry_name: None,
feature_config: &self.current_feature_config,
build_target: self.build_target.as_deref(),
},
Expand All @@ -469,6 +477,7 @@ impl Check {
highest_allowed_version: version,
},
name,
registry_name: None,
feature_config: &self.baseline_feature_config,
build_target: self.build_target.as_deref(),
},
Expand Down Expand Up @@ -527,11 +536,25 @@ note: skipped the following crates since they have no library target: {skipped}"
workspace_overrides.as_deref(),
)?;

let source = selected.source.as_ref();
let publish = selected.publish.as_deref();
let published_to_crates_io =
source.map(|s| s.is_crates_io()).unwrap_or_else(|| {
publish.is_none_or(|p| p.iter().any(|name| name == "crates-io"))
});

// registry name is needed to find its configuration in Cargo's config
let registry_name = if published_to_crates_io {
None
} else {
publish.and_then(|registries| registries.first())
};
Ok(Some(CrateToCheck {
overrides,
current_crate_data: CrateDataForRustdoc {
crate_type: rustdoc_gen::CrateType::Current,
name: crate_name.clone(),
registry_name: registry_name.map(From::from),
feature_config: &self.current_feature_config,
build_target: self.build_target.as_deref(),
},
Expand All @@ -540,6 +563,7 @@ note: skipped the following crates since they have no library target: {skipped}"
highest_allowed_version: Some(version.clone()),
},
name: crate_name.clone(),
registry_name: registry_name.map(From::from),
feature_config: &self.baseline_feature_config,
build_target: self.build_target.as_deref(),
},
Expand All @@ -551,14 +575,27 @@ note: skipped the following crates since they have no library target: {skipped}"
}
};

let current_loader = self.get_rustdoc_generator(config, &self.current.source)?;
let baseline_loader = self.get_rustdoc_generator(config, &self.baseline.source)?;
// Each crate in the workspace may use a different registry
let mut generators = HashMap::new();

// Create a report for each crate.
// We want to run all the checks, even if one returns `Err`.
let all_outcomes: Vec<anyhow::Result<(String, CrateReport)>> = crates_to_check
.into_iter()
.iter()
.map(|selected| {
let current_loader = self.get_rustdoc_generator_for_crate(
&mut generators,
config,
&self.current.source,
&selected.current_crate_data,
)?;
let baseline_loader = self.get_rustdoc_generator_for_crate(
&mut generators,
config,
&self.baseline.source,
&selected.baseline_crate_data,
)?;

let start = std::time::Instant::now();
let name = selected.current_crate_data.name.clone();

Expand Down Expand Up @@ -598,6 +635,36 @@ note: skipped the following crates since they have no library target: {skipped}"

Ok(Report { crate_reports })
}

fn get_rustdoc_generator_for_crate<'s>(
&self,
loaders: &mut HashMap<(&'s RustdocSource, Option<&'s str>), Rc<dyn RustdocGenerator>>,
config: &mut GlobalConfig,
source: &'s RustdocSource,
crate_data: &'s CrateDataForRustdoc<'_>,
) -> Result<Rc<dyn RustdocGenerator>, anyhow::Error> {
let registry_name = if matches!(source, RustdocSource::VersionFromRegistry(_)) {
crate_data.registry_name.as_deref()
} else {
None
};
Ok(match loaders.entry((source, registry_name)) {
Entry::Occupied(occupied_entry) => occupied_entry.get().clone(),
Entry::Vacant(vacant_entry) => {
if let Some(registry_name) = registry_name {
config.log_verbose(|config| {
config.shell_note(format_args!(
"Using a custom registry '{registry_name}' (because of '{}')",
crate_data.name,
))
})?;
}
vacant_entry
.insert(self.get_rustdoc_generator(config, source, registry_name)?)
.clone()
}
})
}
}

fn overrides_for_workspace_package(
Expand Down
Loading