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
50 changes: 20 additions & 30 deletions crates/rattler_lock/src/conda.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,50 +157,40 @@ pub enum GitShallowSpec {
Rev,
}

/// Package build source kind.
/// Package build source location for reproducible builds.
///
/// This stores the exact source location information needed to
/// reproducibly build a package from source. Used by pixi build
/// and other package building tools.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PackageBuildSourceKind {
/// Git repository source with specific revision
pub enum PackageBuildSource {
/// Git repository source with specific revision.
Git {
/// The repository URL
/// The repository URL.
url: Url,
/// Shallow specification of repository head to use.
///
/// Needed to detect if we have to recompute revision.
spec: Option<GitShallowSpec>,
/// The specific git revision.
rev: String,
/// Subdirectory on which focus on.
subdir: Option<Utf8TypedPathBuf>,
},
/// URL-based archive source with content hash
/// URL-based archive source with content hash.
Url {
/// The URL to the archive
/// The URL to the archive.
url: Url,
/// The SHA256 hash of the archive content
/// The SHA256 hash of the archive content.
sha256: Sha256Hash,
/// Subdirectory on which focus on.
subdir: Option<Utf8TypedPathBuf>,
},
/// Source is some local path.
Path {
/// Actual path.
path: Utf8TypedPathBuf,
},
}

/// Package build source location for reproducible builds.
///
/// This stores the exact source location information needed to
/// reproducibly build a package from source. Used by pixi build
/// and other package building tools.
///
/// There are 3 different types of locations: path, git, url
/// (archive). We store only git and url sources, since path-based
/// sources can change over time and would require expensive
/// computation of directory file hashes for reproducibility.
///
/// For git sources we store the repository url and exact revision.
/// For url sources we store the archive url and its content hash.
///
/// For both kinds we store subdirectory.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PackageBuildSource {
/// Kind of source we fetch.
pub kind: PackageBuildSourceKind,
/// Subdirectory of the source from which build starts.
pub subdirectory: Option<Utf8TypedPathBuf>,
}

/// Information about a source package stored in the lock-file.
Expand Down
2 changes: 1 addition & 1 deletion crates/rattler_lock/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ pub use builder::{LockFileBuilder, LockedPackage};
pub use channel::Channel;
pub use conda::{
CondaBinaryData, CondaPackageData, CondaSourceData, ConversionError, GitShallowSpec, InputHash,
PackageBuildSource, PackageBuildSourceKind,
PackageBuildSource,
};
pub use file_format_version::FileFormatVersion;
pub use hash::PackageHashes;
Expand Down
73 changes: 52 additions & 21 deletions crates/rattler_lock/src/parse/models/v6/source_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use serde_with::{serde_as, DeserializeAs, SerializeAs};
use typed_path::Utf8TypedPathBuf;
use url::Url;

use crate::conda::{GitShallowSpec, PackageBuildSource, PackageBuildSourceKind};
use crate::conda::{GitShallowSpec, PackageBuildSource};
use crate::source::{
GitReference, GitSourceLocation, PathSourceLocation, SourceLocation, UrlSourceLocation,
};
Expand Down Expand Up @@ -114,6 +114,9 @@ pub enum SourceLocationError {

#[error("must specify none or exactly one of `branch`, `tag` or `rev`")]
MultipleGitReferences,

#[error("`path` can not have `subdir`")]
PathSubdir,
}

impl<'a> TryFrom<SourceLocationData<'a>> for SourceLocation {
Expand Down Expand Up @@ -144,6 +147,9 @@ impl<'a> TryFrom<SourceLocationData<'a>> for SourceLocation {
let url = url.into_owned();
Ok(SourceLocation::Url(UrlSourceLocation { url, md5, sha256 }))
} else if let Some(path) = path {
if subdirectory.is_some() {
return Err(SourceLocationError::PathSubdir);
}
let path = path.into_owned().into();
Ok(SourceLocation::Path(PathSourceLocation { path }))
} else if let Some(git) = git {
Expand Down Expand Up @@ -187,14 +193,22 @@ struct PackageBuildSourceData<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub rev: Option<Cow<'a, str>>,

#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<Cow<'a, str>>,

#[serde(skip_serializing_if = "Option::is_none")]
pub subdir: Option<Cow<'a, str>>,
}

impl<'a> From<&'a PackageBuildSource> for PackageBuildSourceData<'a> {
fn from(value: &'a PackageBuildSource) -> Self {
match &value.kind {
PackageBuildSourceKind::Git { url, spec, rev } => {
match value {
PackageBuildSource::Git {
url,
spec,
rev,
subdir,
} => {
let (branch, tag, explicit_rev) = match spec {
Some(GitShallowSpec::Branch(branch)) => {
(Some(Cow::Borrowed(branch.as_str())), None, None)
Expand All @@ -205,29 +219,43 @@ impl<'a> From<&'a PackageBuildSource> for PackageBuildSourceData<'a> {
Some(GitShallowSpec::Rev) => (None, None, Some(true)),
None => (None, None, None),
};
let subdir = value
.subdirectory
.as_ref()
.map(|p| Cow::Borrowed(p.as_str()));
let subdir = subdir.as_ref().map(|p| Cow::Borrowed(p.as_str()));
Self {
url: None,
sha256: None,
git: Some(Cow::Borrowed(url)),
branch,
tag,
explicit_rev,
subdir,
path: None,
rev: Some(Cow::Borrowed(rev)),
subdir,
}
}
PackageBuildSourceKind::Url { url, sha256 } => Self {
PackageBuildSource::Url {
url,
sha256,
subdir,
} => Self {
url: Some(Cow::Borrowed(url)),
sha256: Some(*sha256),
git: None,
branch: None,
tag: None,
explicit_rev: None,
rev: None,
path: None,
subdir: subdir.as_ref().map(|p| Cow::Borrowed(p.as_str())),
},
PackageBuildSource::Path { path } => Self {
url: None,
sha256: None,
git: None,
branch: None,
tag: None,
explicit_rev: None,
rev: None,
path: Some(Cow::Borrowed(path.as_str())),
subdir: None,
},
}
Expand Down Expand Up @@ -261,25 +289,28 @@ impl<'a> TryFrom<PackageBuildSourceData<'a>> for PackageBuildSource {
tag,
rev,
explicit_rev,
path,
subdir,
} = value;

let count = [url.is_some(), git.is_some()]
let count = [url.is_some(), git.is_some(), path.is_some()]
.into_iter()
.filter(|&x| x)
.count();
if count != 1 {
return Err(PackageBuildSourceError::MissingOrMultipleSourceRoots);
}

let subdirectory = subdir.map(|s| Utf8TypedPathBuf::from(&*s));
let path = path.map(|s| Utf8TypedPathBuf::from(&*s));
let subdir = subdir.map(|s| Utf8TypedPathBuf::from(&*s));

if let Some(url) = url {
let url = url.into_owned();
let sha256 = sha256.ok_or(PackageBuildSourceError::MissingSha256ForUrl)?;
Ok(PackageBuildSource {
kind: PackageBuildSourceKind::Url { url, sha256 },
subdirectory,
Ok(PackageBuildSource::Url {
url,
sha256,
subdir,
})
} else if let Some(git) = git {
let git = git.into_owned();
Expand All @@ -301,14 +332,14 @@ impl<'a> TryFrom<PackageBuildSourceData<'a>> for PackageBuildSource {
None
};

Ok(PackageBuildSource {
kind: PackageBuildSourceKind::Git {
url: git,
spec,
rev,
},
subdirectory,
Ok(PackageBuildSource::Git {
url: git,
spec,
rev,
subdir,
})
} else if let Some(path) = path {
Ok(PackageBuildSource::Path { path })
} else {
unreachable!("we already checked that exactly one of url or git is set")
}
Expand Down