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

feat: add direct url repodata building #725

Merged
merged 54 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
a52ac91
wip: allow for repodata to be created from a url based dependency
ruben-arts Jun 4, 2024
5eb36b3
fix: local test, ignore lineno in env
ruben-arts Jun 5, 2024
2793fc8
feat: implement direct_url_query for fetching package from url and ge…
ruben-arts Jun 5, 2024
5ee5d0f
feat: solve for direct url
ruben-arts Jun 5, 2024
88cba3a
fmt
ruben-arts Jun 5, 2024
35492b2
fix: test, download example package
ruben-arts Jun 5, 2024
6cd59d8
fmt
ruben-arts Jun 5, 2024
8b573c6
fix: test, use same archive type
ruben-arts Jun 5, 2024
a3da32f
fix: repodata result, tested in py-rattler
ruben-arts Jun 5, 2024
1b9d3fc
fix: test, use temp_dir
ruben-arts Jun 5, 2024
df6b092
fmt
ruben-arts Jun 5, 2024
a54111a
fix: build/test
ruben-arts Jun 10, 2024
3c74946
feat: add matcher trait to remove direct url from package record
ruben-arts Jun 10, 2024
f05d3ea
fix: remove wrong defaults
ruben-arts Jun 10, 2024
0b3e6df
Merge branch 'main' into feature/direct_url_query
ruben-arts Jun 10, 2024
26dfc1c
feat: move the direct url query into a future which is called by the …
ruben-arts Jun 10, 2024
1c41a89
fix: tests
ruben-arts Jun 10, 2024
4a7f67d
fix: speed back up matches
ruben-arts Jun 10, 2024
cb45f15
fix: module rename to direct url query
ruben-arts Jun 10, 2024
2749332
fix: improve direct url error
ruben-arts Jun 10, 2024
7e93696
fix: share package cache in the inner gateway
ruben-arts Jun 10, 2024
55f9e1e
fix: py-rattler
ruben-arts Jun 10, 2024
1e5e5c4
misc: add package cache to gateway builder
ruben-arts Jun 12, 2024
dbc4838
misc: add error for missing name in matchspec
ruben-arts Jun 12, 2024
d520aa0
misc: check package name compatibility between package from url and t…
ruben-arts Jun 12, 2024
f34afe5
fix: remove dbg!
ruben-arts Jun 12, 2024
98cc73f
fix: remove unsafe check from root req
ruben-arts Jun 12, 2024
179b126
misc: print with direct url in create command
ruben-arts Jun 12, 2024
c126693
fix: add direct url specific subdir
ruben-arts Jun 12, 2024
b108da2
Merge branch 'main' into feature/direct_url_query
ruben-arts Jun 13, 2024
885ea5b
fmt
ruben-arts Jun 13, 2024
27bf7e2
fix: use correct dir for cache and make a const for pkgs and repodata…
ruben-arts Jun 13, 2024
9f0cd3d
fix: merge conflict
ruben-arts Jun 13, 2024
304a838
fmt
ruben-arts Jun 13, 2024
d03a1d3
clippy
ruben-arts Jun 13, 2024
c868b5a
Update crates/rattler_repodata_gateway/src/gateway/error.rs
ruben-arts Jun 13, 2024
cce373a
Update crates/rattler_repodata_gateway/src/gateway/query.rs
ruben-arts Jun 13, 2024
87c8b5a
fix: Make the direct url the first subdir
ruben-arts Jun 13, 2024
c296eda
fix: Make the direct url the first subdir
ruben-arts Jun 13, 2024
600ed22
fix: reduce amount of name checking by providing url in direct_url_specs
ruben-arts Jun 13, 2024
7a19f32
fix tests
ruben-arts Jun 13, 2024
97d3cbd
fmt
ruben-arts Jun 13, 2024
cb7a0cc
fix: subdirs only used for channels but in the result reserve a spot …
ruben-arts Jun 13, 2024
d2404de
fix: docstring
ruben-arts Jun 13, 2024
0f09dd8
fix: make channel nameless for direct urls
ruben-arts Jun 13, 2024
bee860c
fix intra-docs test
ruben-arts Jun 13, 2024
b7eeede
fix: match on record not package record
ruben-arts Jun 13, 2024
7ea414a
fix: improve error name and only unpack spec once.
ruben-arts Jun 13, 2024
44c2864
fix: Add new subdir with offset
ruben-arts Jun 13, 2024
268df39
fmt
ruben-arts Jun 13, 2024
4cc3c50
test: subdir asignment for direct url
ruben-arts Jun 13, 2024
e2b2848
fmt
ruben-arts Jun 13, 2024
e74d4dc
fix: windows issues
baszalmstra Jun 13, 2024
8ab4d68
fix: clippy
baszalmstra Jun 13, 2024
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
1 change: 1 addition & 0 deletions crates/rattler-bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ rattler_networking = { path="../rattler_networking", version = "0.20.8", default
rattler_repodata_gateway = { path="../rattler_repodata_gateway", version = "0.20.5", default-features = false, features = ["gateway"] }
rattler_solve = { path="../rattler_solve", version = "0.24.2", default-features = false, features = ["resolvo", "libsolv_c"] }
rattler_virtual_packages = { path="../rattler_virtual_packages", version = "0.19.15", default-features = false }
rattler_cache = { path="../rattler_cache", version = "0.1.0", default-features = false }
reqwest = { workspace = true }
reqwest-middleware = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
Expand Down
19 changes: 15 additions & 4 deletions crates/rattler-bin/src/commands/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use clap::ValueEnum;
use indicatif::{ProgressBar, ProgressStyle};
use itertools::Itertools;
use rattler::install::{IndicatifReporter, Installer};
use rattler::package_cache::PackageCache;
use rattler::{
default_cache_dir,
install::{Transaction, TransactionOperation},
Expand Down Expand Up @@ -143,7 +144,10 @@ pub async fn create(opt: Opt) -> anyhow::Result<()> {

// Get the package names from the matchspecs so we can only load the package records that we need.
let gateway = Gateway::builder()
.with_cache_dir(cache_dir.join("repodata"))
.with_cache_dir(cache_dir.join(rattler_cache::REPODATA_CACHE_DIR))
.with_package_cache(PackageCache::new(
cache_dir.join(rattler_cache::PACKAGE_CACHE_DIR),
))
.with_client(download_client.clone())
.finish();

Expand Down Expand Up @@ -204,7 +208,7 @@ pub async fn create(opt: Opt) -> anyhow::Result<()> {
"Virtual packages:\n{}\n",
virtual_packages
.iter()
.format_with("\n", |i, f| f(&format_args!(" - {i}",)),)
.format_with("\n", |i, f| f(&format_args!(" - {i}",)))
);

// Now that we parsed and downloaded all information, construct the packaging problem that we
Expand Down Expand Up @@ -283,11 +287,18 @@ pub async fn create(opt: Opt) -> anyhow::Result<()> {
/// Prints the operations of the transaction to the console.
fn print_transaction(transaction: &Transaction<PrefixRecord, RepoDataRecord>) {
let format_record = |r: &RepoDataRecord| {
let direct_url_print = if r.clone().channel.is_empty() {
r.url.as_str()
} else {
""
};

format!(
"{} {} {}",
"{} {} {} {}",
r.package_record.name.as_normalized(),
r.package_record.version,
r.package_record.build
r.package_record.build,
direct_url_print,
)
};

Expand Down
2 changes: 1 addition & 1 deletion crates/rattler/src/install/installer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ impl Installer {
PackageCache::new(
default_cache_dir()
.expect("failed to determine default cache directory")
.join("pkgs"),
.join(rattler_cache::PACKAGE_CACHE_DIR),
)
});

Expand Down
4 changes: 4 additions & 0 deletions crates/rattler_cache/src/consts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/// The location in the main cache folder where the conda package cache is stored.
pub const PACKAGE_CACHE_DIR: &str = "pkgs";
/// The location in the main cache folder where the repodata cache is stored.
pub const REPODATA_CACHE_DIR: &str = "repodata";
4 changes: 4 additions & 0 deletions crates/rattler_cache/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ use std::path::PathBuf;
pub mod package_cache;

pub mod validation;

mod consts;
pub use consts::{PACKAGE_CACHE_DIR, REPODATA_CACHE_DIR};

/// Returns the default cache directory used by rattler.
pub fn default_cache_dir() -> anyhow::Result<PathBuf> {
Ok(dirs::cache_dir()
Expand Down
16 changes: 15 additions & 1 deletion crates/rattler_cache/src/package_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ pub struct CacheKey {
sha256: Option<Sha256Hash>,
}

impl CacheKey {
/// Adds a sha256 hash of the archive.
pub fn with_sha256(mut self, sha256: Sha256Hash) -> Self {
self.sha256 = Some(sha256);
self
}

/// Potentially adds a sha256 hash of the archive.
pub fn with_opt_sha256(mut self, sha256: Option<Sha256Hash>) -> Self {
self.sha256 = sha256;
self
}
}

impl CacheKey {
/// Return the sha256 hash of the package if it is known.
pub fn sha256(&self) -> Option<Sha256Hash> {
Expand Down Expand Up @@ -277,7 +291,7 @@ impl PackageCache {
return Err(err);
}

// Determine whether or not to retry based on the retry policy
// Determine whether to retry based on the retry policy
let execute_after = match retry_policy.should_retry(request_start, current_try) {
RetryDecision::Retry { execute_after } => execute_after,
RetryDecision::DoNotRetry => return Err(err),
Expand Down
2 changes: 1 addition & 1 deletion crates/rattler_conda_types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub use explicit_environment_spec::{
pub use generic_virtual_package::GenericVirtualPackage;
pub use match_spec::matcher::{StringMatcher, StringMatcherParseError};
pub use match_spec::parse::ParseMatchSpecError;
pub use match_spec::{MatchSpec, NamelessMatchSpec};
pub use match_spec::{MatchSpec, Matches, NamelessMatchSpec};
pub use no_arch_type::{NoArchKind, NoArchType};
pub use package_name::{InvalidPackageNameError, PackageName};
pub use parse_mode::ParseStrictness;
Expand Down
232 changes: 158 additions & 74 deletions crates/rattler_conda_types/src/match_spec/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{build_spec::BuildNumberSpec, PackageName, PackageRecord, VersionSpec};
use crate::{build_spec::BuildNumberSpec, PackageName, PackageRecord, RepoDataRecord, VersionSpec};
use rattler_digest::{serde::SerializableHash, Md5Hash, Sha256Hash};
use serde::{Deserialize, Deserializer, Serialize};
use serde_with::{serde_as, skip_serializing_none, DisplayFromStr};
Expand Down Expand Up @@ -196,47 +196,6 @@ impl Display for MatchSpec {
}

impl MatchSpec {
/// Match a [`MatchSpec`] against a [`PackageRecord`]
pub fn matches(&self, record: &PackageRecord) -> bool {
if let Some(name) = self.name.as_ref() {
if name != &record.name {
return false;
}
}

if let Some(spec) = self.version.as_ref() {
if !spec.matches(&record.version) {
return false;
}
}

if let Some(build_string) = self.build.as_ref() {
if !build_string.matches(&record.build) {
return false;
}
}

if let Some(build_number) = self.build_number.as_ref() {
if !build_number.matches(&record.build_number) {
return false;
}
}

if let Some(md5_spec) = self.md5.as_ref() {
if Some(md5_spec) != record.md5.as_ref() {
return false;
}
}

if let Some(sha256_spec) = self.sha256.as_ref() {
if Some(sha256_spec) != record.sha256.as_ref() {
return false;
}
}

true
}

/// Decomposes this instance into a [`NamelessMatchSpec`] and a name.
pub fn into_nameless(self) -> (Option<PackageName>, NamelessMatchSpec) {
(
Expand Down Expand Up @@ -300,37 +259,6 @@ pub struct NamelessMatchSpec {
pub url: Option<Url>,
}

impl NamelessMatchSpec {
/// Match a [`MatchSpec`] against a [`PackageRecord`]
pub fn matches(&self, record: &PackageRecord) -> bool {
if let Some(spec) = self.version.as_ref() {
if !spec.matches(&record.version) {
return false;
}
}

if let Some(build_string) = self.build.as_ref() {
if !build_string.matches(&record.build) {
return false;
}
}

if let Some(md5_spec) = self.md5.as_ref() {
if Some(md5_spec) != record.md5.as_ref() {
return false;
}
}

if let Some(sha256_spec) = self.sha256.as_ref() {
if Some(sha256_spec) != record.sha256.as_ref() {
return false;
}
}

true
}
}

impl Display for NamelessMatchSpec {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match &self.version {
Expand Down Expand Up @@ -419,14 +347,136 @@ where
}
}

/// A trait that defines the behavior of matching a spec against a record.
pub trait Matches<T> {
/// Match a [`MatchSpec`] against a record.
/// Matching it to a record means that the record is valid for the spec.
fn matches(&self, other: &T) -> bool;
}

impl Matches<PackageRecord> for NamelessMatchSpec {
/// Match a [`NamelessMatchSpec`] against a [`PackageRecord`]
fn matches(&self, other: &PackageRecord) -> bool {
if let Some(spec) = self.version.as_ref() {
if !spec.matches(&other.version) {
return false;
}
}

if let Some(build_string) = self.build.as_ref() {
if !build_string.matches(&other.build) {
return false;
}
}

if let Some(build_number) = self.build_number.as_ref() {
if !build_number.matches(&other.build_number) {
return false;
}
}

if let Some(md5_spec) = self.md5.as_ref() {
if Some(md5_spec) != other.md5.as_ref() {
return false;
}
}

if let Some(sha256_spec) = self.sha256.as_ref() {
if Some(sha256_spec) != other.sha256.as_ref() {
return false;
}
}

true
}
}

impl Matches<PackageRecord> for MatchSpec {
/// Match a [`MatchSpec`] against a [`PackageRecord`]
fn matches(&self, other: &PackageRecord) -> bool {
if let Some(name) = self.name.as_ref() {
if name != &other.name {
return false;
}
}

if let Some(spec) = self.version.as_ref() {
if !spec.matches(&other.version) {
return false;
}
}

if let Some(build_string) = self.build.as_ref() {
if !build_string.matches(&other.build) {
return false;
}
}

if let Some(build_number) = self.build_number.as_ref() {
if !build_number.matches(&other.build_number) {
return false;
}
}

if let Some(md5_spec) = self.md5.as_ref() {
if Some(md5_spec) != other.md5.as_ref() {
return false;
}
}

if let Some(sha256_spec) = self.sha256.as_ref() {
if Some(sha256_spec) != other.sha256.as_ref() {
return false;
}
}

true
}
}

impl Matches<RepoDataRecord> for MatchSpec {
/// Match a [`MatchSpec`] against a [`RepoDataRecord`]
fn matches(&self, other: &RepoDataRecord) -> bool {
if let Some(url_spec) = self.url.as_ref() {
if url_spec != &other.url {
return false;
}
}

if !self.matches(&other.package_record) {
return false;
}

true
}
}

impl Matches<RepoDataRecord> for NamelessMatchSpec {
/// Match a [`NamelessMatchSpec`] against a [`RepoDataRecord`]
fn matches(&self, other: &RepoDataRecord) -> bool {
if let Some(url_spec) = self.url.as_ref() {
if url_spec != &other.url {
return false;
}
}

if !self.matches(&other.package_record) {
return false;
}

true
}
}

#[cfg(test)]
mod tests {
use std::str::FromStr;

use rattler_digest::{parse_digest_from_hex, Md5, Sha256};

use crate::{
MatchSpec, NamelessMatchSpec, PackageName, PackageRecord, ParseStrictness::*, Version,
match_spec::Matches, MatchSpec, NamelessMatchSpec, PackageName, PackageRecord,
ParseStrictness::*, RepoDataRecord, Version,
};
use insta::assert_snapshot;
use std::hash::{Hash, Hasher};
Expand Down Expand Up @@ -524,6 +574,40 @@ mod tests {
assert!(!spec.matches(&record));
}

#[test]
fn test_layered_matches() {
let repodata_record = RepoDataRecord {
package_record: PackageRecord::new(
PackageName::new_unchecked("mamba"),
Version::from_str("1.0").unwrap(),
String::from(""),
),
file_name: String::from("mamba-1.0-py37_0"),
url: url::Url::parse("https://mamba.io/mamba-1.0-py37_0.conda").unwrap(),
channel: String::from("mamba"),
};
let package_record = repodata_record.clone().package_record;

// Test with basic spec
let match_spec = MatchSpec::from_str("mamba[version==1.0]", Strict).unwrap();
let nameless_spec = match_spec.clone().into_nameless().1;

assert!(match_spec.matches(&repodata_record));
assert!(match_spec.matches(&package_record));
assert!(nameless_spec.matches(&repodata_record));
assert!(nameless_spec.matches(&package_record));

// Test with url spec
let match_spec =
MatchSpec::from_str("https://mamba.io/mamba-1.0-py37_0.conda", Strict).unwrap();
let nameless_spec = match_spec.clone().into_nameless().1;

assert!(match_spec.matches(&repodata_record));
assert!(match_spec.matches(&package_record));
assert!(nameless_spec.matches(&repodata_record));
assert!(nameless_spec.matches(&package_record));
}

#[test]
fn test_serialize_matchspec() {
let specs = ["mamba 1.0 py37_0",
Expand Down
Loading
Loading