feat(source): support cherry-picking commits for git sources
- add source.cherry_pick (and cherry-pick alias) to package spec parsing/serialization - apply listed cherry-picks in order after git checkout - reject cherry_pick on non-git sources with explicit error - include cherry_pick in interactive spec TOML output - add parsing/behavior tests and update Source test fixtures
This commit is contained in:
@@ -81,6 +81,7 @@ mod tests {
|
|||||||
extract_dir: "e".into(),
|
extract_dir: "e".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Bin,
|
build_type: BuildType::Bin,
|
||||||
|
|||||||
@@ -283,6 +283,7 @@ mod tests {
|
|||||||
extract_dir: "e".into(),
|
extract_dir: "e".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Custom,
|
build_type: BuildType::Custom,
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ mod tests {
|
|||||||
extract_dir: "e".into(),
|
extract_dir: "e".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Makefile,
|
build_type: BuildType::Makefile,
|
||||||
|
|||||||
@@ -323,6 +323,7 @@ mod tests {
|
|||||||
extract_dir: "e".into(),
|
extract_dir: "e".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Meson,
|
build_type: BuildType::Meson,
|
||||||
|
|||||||
@@ -267,6 +267,7 @@ mod tests {
|
|||||||
extract_dir: "src".into(),
|
extract_dir: "src".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Custom,
|
build_type: BuildType::Custom,
|
||||||
|
|||||||
@@ -721,6 +721,7 @@ mod tests {
|
|||||||
extract_dir: "src".into(),
|
extract_dir: "src".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Python,
|
build_type: BuildType::Python,
|
||||||
|
|||||||
@@ -490,6 +490,7 @@ mod tests {
|
|||||||
extract_dir: "foo".into(),
|
extract_dir: "foo".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Custom,
|
build_type: BuildType::Custom,
|
||||||
|
|||||||
@@ -375,6 +375,7 @@ mod tests {
|
|||||||
extract_dir: "foo".into(),
|
extract_dir: "foo".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: crate::package::Build {
|
build: crate::package::Build {
|
||||||
build_type: crate::package::BuildType::Custom,
|
build_type: crate::package::BuildType::Custom,
|
||||||
@@ -415,6 +416,7 @@ mod tests {
|
|||||||
extract_dir: "foo".into(),
|
extract_dir: "foo".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: crate::package::Build {
|
build: crate::package::Build {
|
||||||
build_type: crate::package::BuildType::Custom,
|
build_type: crate::package::BuildType::Custom,
|
||||||
@@ -463,6 +465,7 @@ mod tests {
|
|||||||
extract_dir: "foo".into(),
|
extract_dir: "foo".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: crate::package::Build {
|
build: crate::package::Build {
|
||||||
build_type: crate::package::BuildType::Custom,
|
build_type: crate::package::BuildType::Custom,
|
||||||
|
|||||||
@@ -636,6 +636,7 @@ mod tests {
|
|||||||
extract_dir: "foo".into(),
|
extract_dir: "foo".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Custom,
|
build_type: BuildType::Custom,
|
||||||
|
|||||||
@@ -425,6 +425,7 @@ pub fn create_interactive() -> Result<PackageSpec> {
|
|||||||
extract_dir,
|
extract_dir,
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if show_advanced {
|
if show_advanced {
|
||||||
@@ -791,6 +792,17 @@ pub fn spec_to_minimal_toml(spec: &PackageSpec) -> anyhow::Result<String> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if !s.cherry_pick.is_empty() {
|
||||||
|
st.insert(
|
||||||
|
"cherry_pick".into(),
|
||||||
|
Value::Array(
|
||||||
|
s.cherry_pick
|
||||||
|
.iter()
|
||||||
|
.map(|rev| Value::String(rev.clone()))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
arr.push(Value::Table(st));
|
arr.push(Value::Table(st));
|
||||||
}
|
}
|
||||||
root.insert("source".into(), Value::Array(arr));
|
root.insert("source".into(), Value::Array(arr));
|
||||||
@@ -1316,6 +1328,7 @@ mod tests {
|
|||||||
extract_dir: "foo-1.0".into(),
|
extract_dir: "foo-1.0".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Autotools,
|
build_type: BuildType::Autotools,
|
||||||
@@ -1365,6 +1378,7 @@ mod tests {
|
|||||||
extract_dir: "foo-1.0".into(),
|
extract_dir: "foo-1.0".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Autotools,
|
build_type: BuildType::Autotools,
|
||||||
@@ -1401,6 +1415,7 @@ mod tests {
|
|||||||
extract_dir: "foo-1.0".into(),
|
extract_dir: "foo-1.0".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Autotools,
|
build_type: BuildType::Autotools,
|
||||||
@@ -1464,6 +1479,7 @@ mod tests {
|
|||||||
extract_dir: "foo-1.0".into(),
|
extract_dir: "foo-1.0".into(),
|
||||||
patches: vec!["fix.patch".into()],
|
patches: vec!["fix.patch".into()],
|
||||||
post_extract: vec!["autoreconf -fi".into()],
|
post_extract: vec!["autoreconf -fi".into()],
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Autotools,
|
build_type: BuildType::Autotools,
|
||||||
@@ -1525,6 +1541,7 @@ mod tests {
|
|||||||
extract_dir: "$name-$version".into(),
|
extract_dir: "$name-$version".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Autotools,
|
build_type: BuildType::Autotools,
|
||||||
@@ -1560,6 +1577,7 @@ mod tests {
|
|||||||
extract_dir: "foo-1.0".into(),
|
extract_dir: "foo-1.0".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Autotools,
|
build_type: BuildType::Autotools,
|
||||||
|
|||||||
@@ -1153,6 +1153,7 @@ type = "custom"
|
|||||||
);
|
);
|
||||||
assert!(spec.sources()[0].patches.is_empty());
|
assert!(spec.sources()[0].patches.is_empty());
|
||||||
assert!(spec.sources()[0].post_extract.is_empty());
|
assert!(spec.sources()[0].post_extract.is_empty());
|
||||||
|
assert!(spec.sources()[0].cherry_pick.is_empty());
|
||||||
assert_eq!(spec.spec_dir, tmp.path());
|
assert_eq!(spec.spec_dir, tmp.path());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1193,6 +1194,40 @@ type = "custom"
|
|||||||
assert_eq!(spec.sources()[1].extract_dir, "bar");
|
assert_eq!(spec.sources()[1].extract_dir, "bar");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_git_source_with_cherry_pick() {
|
||||||
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
|
let path = tmp.path().join("pkg.toml");
|
||||||
|
|
||||||
|
std::fs::write(
|
||||||
|
&path,
|
||||||
|
r#"
|
||||||
|
[package]
|
||||||
|
name = "foo"
|
||||||
|
version = "1.0"
|
||||||
|
description = "d"
|
||||||
|
homepage = "h"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
[source]
|
||||||
|
url = "https://example.com/foo.git#main"
|
||||||
|
sha256 = "skip"
|
||||||
|
extract_dir = "foo"
|
||||||
|
cherry_pick = ["deadbeef", "cafebabe"]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
type = "custom"
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let spec = PackageSpec::from_file(&path).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
spec.sources()[0].cherry_pick,
|
||||||
|
vec!["deadbeef".to_string(), "cafebabe".to_string()]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_package_dependencies_overrides() {
|
fn parse_package_dependencies_overrides() {
|
||||||
let tmp = tempfile::tempdir().unwrap();
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
@@ -2378,6 +2413,7 @@ type = "custom"
|
|||||||
extract_dir: "e".into(),
|
extract_dir: "e".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Custom,
|
build_type: BuildType::Custom,
|
||||||
@@ -2503,6 +2539,18 @@ pub struct Source {
|
|||||||
/// post_extract = ["autoreconf -fi"]
|
/// post_extract = ["autoreconf -fi"]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub post_extract: Vec<String>,
|
pub post_extract: Vec<String>,
|
||||||
|
|
||||||
|
/// Optional list of git commit hashes/revs to cherry-pick after checkout.
|
||||||
|
///
|
||||||
|
/// This is only valid for git sources (`*.git` URL or `url#rev` git form).
|
||||||
|
/// Example:
|
||||||
|
/// cherry_pick = ["a1b2c3d4", "deadbeef"]
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
alias = "cherry-pick",
|
||||||
|
deserialize_with = "deserialize_string_or_array"
|
||||||
|
)]
|
||||||
|
pub cherry_pick: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Manual source copied before standard source fetching.
|
/// Manual source copied before standard source fetching.
|
||||||
|
|||||||
@@ -746,6 +746,7 @@ mod tests {
|
|||||||
extract_dir: "foo".into(),
|
extract_dir: "foo".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Autotools,
|
build_type: BuildType::Autotools,
|
||||||
|
|||||||
+117
-1
@@ -20,6 +20,7 @@ pub fn checkout(
|
|||||||
checkout_dir: &Path,
|
checkout_dir: &Path,
|
||||||
git_cache_dir: &Path,
|
git_cache_dir: &Path,
|
||||||
pkgname: &str,
|
pkgname: &str,
|
||||||
|
cherry_pick_revs: &[String],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
fs::create_dir_all(git_cache_dir).with_context(|| {
|
fs::create_dir_all(git_cache_dir).with_context(|| {
|
||||||
format!(
|
format!(
|
||||||
@@ -51,6 +52,75 @@ pub fn checkout(
|
|||||||
|
|
||||||
let repo = Repository::open(checkout_dir)?;
|
let repo = Repository::open(checkout_dir)?;
|
||||||
checkout_rev(&repo, rev).with_context(|| format!("Failed to checkout revision '{}'", rev))?;
|
checkout_rev(&repo, rev).with_context(|| format!("Failed to checkout revision '{}'", rev))?;
|
||||||
|
apply_cherry_picks(&repo, cherry_pick_revs)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_cherry_picks(repo: &Repository, cherry_pick_revs: &[String]) -> Result<()> {
|
||||||
|
if cherry_pick_revs.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::log_info!("Applying {} git cherry-pick(s)...", cherry_pick_revs.len());
|
||||||
|
|
||||||
|
for rev in cherry_pick_revs {
|
||||||
|
let rev = rev.trim();
|
||||||
|
if rev.is_empty() {
|
||||||
|
anyhow::bail!("Encountered empty entry in source.cherry_pick");
|
||||||
|
}
|
||||||
|
|
||||||
|
let obj = resolve_rev_object(repo, rev)
|
||||||
|
.with_context(|| format!("Could not resolve cherry-pick rev: {}", rev))?;
|
||||||
|
let commit = obj
|
||||||
|
.peel_to_commit()
|
||||||
|
.with_context(|| format!("Could not peel cherry-pick rev to commit: {}", rev))?;
|
||||||
|
repo.cherrypick(&commit, None)
|
||||||
|
.with_context(|| format!("Failed to cherry-pick rev: {}", rev))?;
|
||||||
|
|
||||||
|
let parent = repo
|
||||||
|
.head()
|
||||||
|
.with_context(|| format!("Failed to read HEAD during cherry-pick {}", rev))?
|
||||||
|
.peel_to_commit()
|
||||||
|
.with_context(|| format!("Failed to peel HEAD to commit during {}", rev))?;
|
||||||
|
|
||||||
|
let mut index = repo
|
||||||
|
.index()
|
||||||
|
.with_context(|| format!("Failed to open index after cherry-pick {}", rev))?;
|
||||||
|
if index.has_conflicts() {
|
||||||
|
anyhow::bail!("Cherry-pick produced conflicts for rev {}", rev);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tree_id = index
|
||||||
|
.write_tree()
|
||||||
|
.with_context(|| format!("Failed to write tree after cherry-pick {}", rev))?;
|
||||||
|
let tree = repo
|
||||||
|
.find_tree(tree_id)
|
||||||
|
.with_context(|| format!("Failed to find tree after cherry-pick {}", rev))?;
|
||||||
|
let message = commit.summary().unwrap_or("cherry-pick");
|
||||||
|
let new_head = repo
|
||||||
|
.commit(
|
||||||
|
None,
|
||||||
|
&commit.author(),
|
||||||
|
&commit.committer(),
|
||||||
|
message,
|
||||||
|
&tree,
|
||||||
|
&[&parent],
|
||||||
|
)
|
||||||
|
.with_context(|| format!("Failed to create cherry-pick commit for rev {}", rev))?;
|
||||||
|
repo.set_head_detached(new_head)
|
||||||
|
.with_context(|| format!("Failed to update detached HEAD after cherry-pick {}", rev))?;
|
||||||
|
|
||||||
|
let mut checkout = git2::build::CheckoutBuilder::new();
|
||||||
|
checkout.force();
|
||||||
|
let obj = repo
|
||||||
|
.find_object(new_head, None)
|
||||||
|
.with_context(|| format!("Failed to resolve new HEAD after cherry-pick {}", rev))?;
|
||||||
|
repo.checkout_tree(&obj, Some(&mut checkout))
|
||||||
|
.with_context(|| format!("Failed to update worktree after cherry-pick {}", rev))?;
|
||||||
|
|
||||||
|
crate::log_info!(" cherry_pick: {}", rev);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -352,8 +422,15 @@ mod tests {
|
|||||||
let tree_id = index.write_tree().unwrap();
|
let tree_id = index.write_tree().unwrap();
|
||||||
let tree = repo.find_tree(tree_id).unwrap();
|
let tree = repo.find_tree(tree_id).unwrap();
|
||||||
let sig = git2::Signature::now("depot-test", "depot@example.test").unwrap();
|
let sig = git2::Signature::now("depot-test", "depot@example.test").unwrap();
|
||||||
|
let mut parents = Vec::new();
|
||||||
|
if let Ok(head) = repo.head()
|
||||||
|
&& let Some(oid) = head.target()
|
||||||
|
{
|
||||||
|
parents.push(repo.find_commit(oid).unwrap());
|
||||||
|
}
|
||||||
|
let parent_refs: Vec<&git2::Commit<'_>> = parents.iter().collect();
|
||||||
|
|
||||||
repo.commit(Some("HEAD"), &sig, &sig, "test", &tree, &[])
|
repo.commit(Some("HEAD"), &sig, &sig, "test", &tree, &parent_refs)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,4 +481,43 @@ mod tests {
|
|||||||
checkout_rev(&repo, "feature").unwrap();
|
checkout_rev(&repo, "feature").unwrap();
|
||||||
assert_eq!(repo.head().unwrap().target().unwrap(), commit_oid);
|
assert_eq!(repo.head().unwrap().target().unwrap(), commit_oid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn apply_cherry_picks_applies_commit_in_order() {
|
||||||
|
let temp = tempfile::tempdir().unwrap();
|
||||||
|
let workdir = temp.path().join("repo");
|
||||||
|
std::fs::create_dir_all(&workdir).unwrap();
|
||||||
|
let repo = Repository::init(&workdir).unwrap();
|
||||||
|
|
||||||
|
let base = commit_file(&repo, &workdir, "README", "base");
|
||||||
|
let picked = commit_file(&repo, &workdir, "README", "picked");
|
||||||
|
|
||||||
|
checkout_rev(&repo, &base.to_string()).unwrap();
|
||||||
|
apply_cherry_picks(&repo, &[picked.to_string()]).unwrap();
|
||||||
|
|
||||||
|
let head = repo.head().unwrap().target().unwrap();
|
||||||
|
assert_eq!(head, picked);
|
||||||
|
assert_eq!(
|
||||||
|
std::fs::read_to_string(workdir.join("README")).unwrap(),
|
||||||
|
"picked"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn apply_cherry_picks_errors_for_unknown_rev() {
|
||||||
|
let temp = tempfile::tempdir().unwrap();
|
||||||
|
let workdir = temp.path().join("repo");
|
||||||
|
std::fs::create_dir_all(&workdir).unwrap();
|
||||||
|
let repo = Repository::init(&workdir).unwrap();
|
||||||
|
|
||||||
|
let base = commit_file(&repo, &workdir, "README", "base");
|
||||||
|
checkout_rev(&repo, &base.to_string()).unwrap();
|
||||||
|
|
||||||
|
let err = apply_cherry_picks(&repo, &["deadbeef".to_string()])
|
||||||
|
.expect_err("unknown cherry-pick rev should fail");
|
||||||
|
assert!(
|
||||||
|
err.to_string()
|
||||||
|
.contains("Could not resolve cherry-pick rev")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -310,6 +310,7 @@ mod tests {
|
|||||||
extract_dir: "foo".into(),
|
extract_dir: "foo".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Custom,
|
build_type: BuildType::Custom,
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ pub fn copy_manual_sources(spec: &PackageSpec, cache_dir: &Path, build_dir: &Pat
|
|||||||
extract_dir: "manual-source".to_string(),
|
extract_dir: "manual-source".to_string(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
};
|
};
|
||||||
let fetched =
|
let fetched =
|
||||||
fetcher::fetch_archive(spec, &source, &cache_dir.join("manual"))?;
|
fetcher::fetch_archive(spec, &source, &cache_dir.join("manual"))?;
|
||||||
@@ -299,6 +300,11 @@ fn prepare_one(
|
|||||||
) -> Result<PathBuf> {
|
) -> Result<PathBuf> {
|
||||||
let url = spec.expand_vars(&source.url);
|
let url = spec.expand_vars(&source.url);
|
||||||
let extract_dir_name = spec.expand_vars(&source.extract_dir);
|
let extract_dir_name = spec.expand_vars(&source.extract_dir);
|
||||||
|
let cherry_pick_revs: Vec<String> = source
|
||||||
|
.cherry_pick
|
||||||
|
.iter()
|
||||||
|
.map(|rev| spec.expand_vars(rev))
|
||||||
|
.collect();
|
||||||
|
|
||||||
// Local file:// handling (directories or archives)
|
// Local file:// handling (directories or archives)
|
||||||
if let Some(path_str) = url.strip_prefix("file://") {
|
if let Some(path_str) = url.strip_prefix("file://") {
|
||||||
@@ -358,11 +364,19 @@ fn prepare_one(
|
|||||||
&checkout_dir,
|
&checkout_dir,
|
||||||
&cache_dir.join("git"),
|
&cache_dir.join("git"),
|
||||||
&spec.package.name,
|
&spec.package.name,
|
||||||
|
&cherry_pick_revs,
|
||||||
)?;
|
)?;
|
||||||
hooks::post_extract(spec, source, &checkout_dir, cache_dir)?;
|
hooks::post_extract(spec, source, &checkout_dir, cache_dir)?;
|
||||||
return Ok(checkout_dir);
|
return Ok(checkout_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !source.cherry_pick.is_empty() {
|
||||||
|
bail!(
|
||||||
|
"source.cherry_pick is only supported for git sources (got URL: {})",
|
||||||
|
source.url
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let src_dir = build_dir.join(&extract_dir_name);
|
let src_dir = build_dir.join(&extract_dir_name);
|
||||||
if src_dir.exists() && src_dir.join(".depot_state").exists() {
|
if src_dir.exists() && src_dir.join(".depot_state").exists() {
|
||||||
crate::log_info!(
|
crate::log_info!(
|
||||||
@@ -458,6 +472,7 @@ mod tests {
|
|||||||
extract_dir: "src".into(),
|
extract_dir: "src".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Custom,
|
build_type: BuildType::Custom,
|
||||||
@@ -517,6 +532,7 @@ mod tests {
|
|||||||
extract_dir: "json-$version".into(),
|
extract_dir: "json-$version".into(),
|
||||||
patches: Vec::new(),
|
patches: Vec::new(),
|
||||||
post_extract: Vec::new(),
|
post_extract: Vec::new(),
|
||||||
|
cherry_pick: Vec::new(),
|
||||||
}],
|
}],
|
||||||
build: Build {
|
build: Build {
|
||||||
build_type: BuildType::Custom,
|
build_type: BuildType::Custom,
|
||||||
@@ -539,6 +555,23 @@ mod tests {
|
|||||||
assert_eq!(rev, "0123456789abcdef");
|
assert_eq!(rev, "0123456789abcdef");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn prepare_one_rejects_cherry_pick_for_non_git_sources() {
|
||||||
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
|
let cache_dir = tmp.path().join("cache");
|
||||||
|
let build_dir = tmp.path().join("build");
|
||||||
|
let mut spec = mk_spec_with_manuals(PathBuf::from("."), Vec::new());
|
||||||
|
spec.source[0].url = "https://example.com/foo.tar.gz".into();
|
||||||
|
spec.source[0].cherry_pick = vec!["deadbeef".into()];
|
||||||
|
|
||||||
|
let err = prepare_one(&spec, &spec.source[0], &cache_dir, &build_dir)
|
||||||
|
.expect_err("non-git source with cherry_pick must be rejected");
|
||||||
|
assert!(
|
||||||
|
err.to_string()
|
||||||
|
.contains("source.cherry_pick is only supported for git sources")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn verify_file_hash_accepts_multiple_algorithms() {
|
fn verify_file_hash_accepts_multiple_algorithms() {
|
||||||
use sha2::{Digest, Sha256, Sha512};
|
use sha2::{Digest, Sha256, Sha512};
|
||||||
|
|||||||
Reference in New Issue
Block a user