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

do not prefetch git dependencies #382

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
4 changes: 3 additions & 1 deletion crate2nix/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use std::{
path::Path,
};

use crate::CommitHash;

impl Config {
/// Read config from path.
pub fn read_from_or_default(path: &Path) -> Result<Config, Error> {
Expand Down Expand Up @@ -112,7 +114,7 @@ pub enum Source {
/// E.g. https://github.com/kolloch/crate2nix.git
url: url::Url,
/// The revision hash.
rev: String,
rev: CommitHash,
/// The sha256 of the fetched result.
sha256: String,
},
Expand Down
2 changes: 2 additions & 0 deletions crate2nix/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ pub mod sources;
pub mod test;
pub mod util;

pub use util::CommitHash;

/// The resolved build info and the input for rendering the build.nix.tera template.
#[derive(Debug, Deserialize, Serialize)]
pub struct BuildInfo {
Expand Down
5 changes: 3 additions & 2 deletions crate2nix/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate2nix::CommitHash;
use std::path::{Path, PathBuf};
use structopt::clap::ArgGroup;
use structopt::StructOpt;
Expand Down Expand Up @@ -273,8 +274,8 @@ pub enum SourceAddingCommands {
/// E.g. https://github.com/kolloch/crate2nix.git
url: url::Url,

#[structopt(long = "rev", parse(from_str), help = "The git revision hash.")]
rev: String,
#[structopt(long = "rev", parse(try_from_str = CommitHash::try_from), help = "The git revision hash.")]
rev: CommitHash,
},

#[structopt(
Expand Down
5 changes: 3 additions & 2 deletions crate2nix/src/prefetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,8 @@ impl PrefetchableSource for RegistrySource {

impl PrefetchableSource for GitSource {
fn needs_prefetch(&self) -> bool {
self.sha256.is_none()
// self.rev is sufficient for reproducible fetching, and that field is mandatory
false
}

fn prefetch(&self) -> Result<String, Error> {
Expand All @@ -333,7 +334,7 @@ impl PrefetchableSource for GitSource {
self.url.as_str(),
"--fetch-submodules",
"--rev",
&self.rev,
self.rev.as_ref(),
];

// TODO: --branch-name isn't documented in nix-prefetch-git --help
Expand Down
51 changes: 41 additions & 10 deletions crate2nix/src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use std::path::{Path, PathBuf};
use crate::metadata::IndexedMetadata;
#[cfg(test)]
use crate::test;
use crate::CommitHash;
use crate::GenerateConfig;
use itertools::Itertools;
use std::{collections::btree_map::BTreeMap, fmt::Display};
Expand Down Expand Up @@ -421,7 +422,7 @@ pub struct RegistrySource {
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)]
pub struct GitSource {
pub url: Url,
pub rev: String,
pub rev: CommitHash,
pub r#ref: Option<String>,
pub sha256: Option<String>,
}
Expand Down Expand Up @@ -493,14 +494,43 @@ impl ResolvedSource {
);
}
let mut url = url::Url::parse(&source_string[GIT_SOURCE_PREFIX.len()..])?;
let mut query_pairs = url.query_pairs();
let branch = query_pairs
.find(|(k, _)| k == "branch")
.map(|(_, v)| v.to_string());
let rev = if let Some((_, rev)) = query_pairs.find(|(k, _)| k == "rev") {
rev.to_string()
} else if let Some(rev) = url.fragment() {
rev.to_string()
let query_pairs = url.query_pairs().collect::<HashMap<_, _>>();

// Locked git sources have optional ?branch, ?tag, ?rev, or ?ref query arguments. It is
// important to capture these in case the given commit hash is not reachable from the
// repo's default HEAD. OTOH if no form of ref is given that is an implication by cargo
// that the default HEAD should be fetched.
const REF_PARAMS: [(&str, &str); 4] = [
("branch", "refs/heads/"),
("tag", "refs/tags/"),
("rev", ""),
("ref", ""),
];
let r#ref = REF_PARAMS.iter().find_map(|(key, ref_prefix)| {
let v = query_pairs.get(*key)?;
if CommitHash::parse(v).is_some() {
// Rev is usually a commit hash, but in some cases it can be a ref. Use as a ref
// only if it is **not** a valid commit hash.
return None;
}
Some(format!("{ref_prefix}{v}"))
});

// In locked sources the git commit hash is given as a URL fragment. It sometimes also
// given in a ?rev query argument. But in other cases a ?rev argument might not be a commit
// hash: the [cargo reference docs][] give an example of a rev argument of `"refs/pull/493/head"`.
//
// [cargo reference docs]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html
//
// That example applies to a Cargo.toml manifest - but such rev arguments do seem to be
// preserved in the lock file.
let rev = if let Some(rev) = url.fragment().and_then(CommitHash::parse) {
rev
} else if let Some(rev) = query_pairs
.get("rev")
.and_then(|rev| CommitHash::parse(rev))
{
rev
} else {
return ResolvedSource::fallback_to_local_directory(
config,
Expand All @@ -509,12 +539,13 @@ impl ResolvedSource {
"No git revision found.",
);
};

url.set_query(None);
url.set_fragment(None);
Ok(ResolvedSource::Git(GitSource {
url,
rev,
r#ref: branch,
r#ref,
sha256: None,
}))
}
Expand Down
3 changes: 2 additions & 1 deletion crate2nix/src/sources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
config,
prefetch::PrefetchableSource,
resolve::{CratesIoSource, GitSource, RegistrySource},
CommitHash,
};
use anyhow::{bail, format_err, Context, Error};
use semver::Version;
Expand Down Expand Up @@ -59,7 +60,7 @@ pub fn registry_source(
}

/// Returns the completed Source::Git definition by prefetching the hash.
pub fn git_io_source(url: Url, rev: String) -> Result<config::Source, Error> {
pub fn git_io_source(url: Url, rev: CommitHash) -> Result<config::Source, Error> {
let prefetchable = GitSource {
url: url.clone(),
rev: rev.clone(),
Expand Down
50 changes: 50 additions & 0 deletions crate2nix/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
//! Homeless code. Usually abstract and algorithmic.

use core::{convert::AsRef, fmt::Display};
use std::collections::BTreeSet;

use anyhow::anyhow;
use serde::{Deserialize, Serialize};

/// Return all occurrences of each item after the first.
/// ```
/// use crate2nix::util::find_duplicates;
Expand All @@ -14,3 +18,49 @@ pub fn find_duplicates<'a, T: Ord>(source: impl Iterator<Item = &'a T>) -> Vec<&
let mut seen = BTreeSet::new();
source.filter(|v| !seen.insert(*v)).collect()
}

/// Newtype for a string that has been verified to be a git commit hash, and has been normalized.
#[derive(Clone, Debug, Hash, PartialEq, Eq, Deserialize, Serialize)]
#[serde(try_from = "String")]
pub struct CommitHash(String);

impl CommitHash {
/// If the string contains 40 hexadecimal characters returns a normalized string by trimming
/// leading and trailing whitespace, and converting alphabetical characters to lower case.
pub fn parse(input: &str) -> Option<Self> {
let normalized = input.trim().to_lowercase();
if normalized.len() == 40 && normalized.chars().all(|c| c.is_ascii_hexdigit()) {
Some(CommitHash(normalized))
} else {
None
}
}
}

impl AsRef<str> for CommitHash {
fn as_ref(&self) -> &str {
&self.0
}
}

impl TryFrom<String> for CommitHash {
type Error = anyhow::Error;

fn try_from(value: String) -> Result<Self, Self::Error> {
<Self as TryFrom<&str>>::try_from(&value)
}
}

impl TryFrom<&str> for CommitHash {
type Error = anyhow::Error;

fn try_from(value: &str) -> Result<Self, Self::Error> {
CommitHash::parse(value).ok_or_else(|| anyhow!("value {value} is not a git commit hash"))
}
}

impl Display for CommitHash {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.0)
}
}
7 changes: 4 additions & 3 deletions crate2nix/templates/Cargo.nix.tera
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,13 @@ rec {
src = lib.cleanSourceWith { filter = sourceFilter; src = {{crate.source.LocalDirectory.path | safe}}; };
{%- elif crate.source.Git %}
workspace_member = null;
src = pkgs.fetchgit {
src = builtins.fetchGit {
url = {{crate.source.Git.url}};
rev = {{crate.source.Git.rev}};
{%- if crate.source.Git.sha256 %}
sha256 = {{ crate.source.Git.sha256 }};
{%- if crate.source.Git.ref %}
ref = {{ crate.source.Git.ref }};
{%- endif %}
submodules = true;
};
{%- else %}
src = builtins.throw ''ERROR: Could not resolve source: {{crate.source | json_encode() | safe}}'';
Expand Down
3 changes: 0 additions & 3 deletions sample_projects/bin_with_git_branch_dep/crate-hashes.json

This file was deleted.

8 changes: 4 additions & 4 deletions sample_projects/bin_with_git_submodule_dep/Cargo.nix
Original file line number Diff line number Diff line change
Expand Up @@ -485,10 +485,10 @@ rec {
edition = "2018";
links = "rocksdb";
workspace_member = null;
src = pkgs.fetchgit {
src = builtins.fetchGit {
url = "https://github.com/rust-rocksdb/rust-rocksdb";
rev = "66f04df013b6e6bd42b5a8c353406e09a7c7da2a";
sha256 = "1rchvjrjamdaznx26gy4bmjj10rrf00mgc1wvkc489r9z1nh4h1h";
submodules = true;
};
authors = [
"Karl Hobley <[email protected]>"
Expand Down Expand Up @@ -837,10 +837,10 @@ rec {
version = "0.21.0";
edition = "2018";
workspace_member = null;
src = pkgs.fetchgit {
src = builtins.fetchGit {
url = "https://github.com/rust-rocksdb/rust-rocksdb";
rev = "66f04df013b6e6bd42b5a8c353406e09a7c7da2a";
sha256 = "1rchvjrjamdaznx26gy4bmjj10rrf00mgc1wvkc489r9z1nh4h1h";
submodules = true;
};
authors = [
"Tyler Neely <[email protected]>"
Expand Down
4 changes: 0 additions & 4 deletions sample_projects/bin_with_git_submodule_dep/crate-hashes.json

This file was deleted.

3 changes: 0 additions & 3 deletions sample_projects/bin_with_lib_git_dep/crate-hashes.json

This file was deleted.

12 changes: 6 additions & 6 deletions sample_projects/codegen/Cargo.nix
Original file line number Diff line number Diff line change
Expand Up @@ -249,10 +249,10 @@ rec {
version = "0.9.7";
edition = "2018";
workspace_member = null;
src = pkgs.fetchgit {
src = builtins.fetchGit {
url = "https://github.com/diwic/dbus-rs.git";
rev = "618262f5e3217cdd173d46d705bbac26c5141e21";
sha256 = "0gvhz2knd1k799l7ssh4rdm5qw0vhazzr3bxpmlgq7fhy6hjazrs";
submodules = true;
};
authors = [
"David Henningsson <[email protected]>"
Expand Down Expand Up @@ -287,10 +287,10 @@ rec {
edition = "2018";
crateBin = [];
workspace_member = null;
src = pkgs.fetchgit {
src = builtins.fetchGit {
url = "https://github.com/diwic/dbus-rs.git";
rev = "618262f5e3217cdd173d46d705bbac26c5141e21";
sha256 = "0gvhz2knd1k799l7ssh4rdm5qw0vhazzr3bxpmlgq7fhy6hjazrs";
submodules = true;
};
authors = [
"David Henningsson <[email protected]>"
Expand Down Expand Up @@ -362,10 +362,10 @@ rec {
edition = "2015";
links = "dbus";
workspace_member = null;
src = pkgs.fetchgit {
src = builtins.fetchGit {
url = "https://github.com/diwic/dbus-rs.git";
rev = "618262f5e3217cdd173d46d705bbac26c5141e21";
sha256 = "0gvhz2knd1k799l7ssh4rdm5qw0vhazzr3bxpmlgq7fhy6hjazrs";
submodules = true;
};
authors = [
"David Henningsson <[email protected]>"
Expand Down
5 changes: 0 additions & 5 deletions sample_projects/codegen/crate-hashes.json

This file was deleted.

8 changes: 4 additions & 4 deletions sample_projects/sub_dir_crates/Cargo.nix
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@ rec {
version = "0.1.0";
edition = "2018";
workspace_member = null;
src = pkgs.fetchgit {
src = builtins.fetchGit {
url = "https://github.com/kolloch/with_sub_crates.git";
rev = "f8ad2b98ff0eb5fea4962f55e3ced5b0b5afe973";
sha256 = "0nlw7rg28p6bya040cbipq4jdcdp4h3q9shdjygfk2xkva9bjl8w";
submodules = true;
};
authors = [
"Peter Kolloch <[email protected]>"
Expand All @@ -108,10 +108,10 @@ rec {
version = "0.1.0";
edition = "2018";
workspace_member = null;
src = pkgs.fetchgit {
src = builtins.fetchGit {
url = "https://github.com/kolloch/with_sub_crates.git";
rev = "f8ad2b98ff0eb5fea4962f55e3ced5b0b5afe973";
sha256 = "0nlw7rg28p6bya040cbipq4jdcdp4h3q9shdjygfk2xkva9bjl8w";
submodules = true;
};
authors = [
"Peter Kolloch <[email protected]>"
Expand Down
4 changes: 0 additions & 4 deletions sample_projects/sub_dir_crates/crate-hashes.json

This file was deleted.

Loading
Loading