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

Update the lock file when necessary #110

Merged
merged 2 commits into from
Oct 30, 2023
Merged
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
19 changes: 15 additions & 4 deletions src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ pub struct Protofetch {
cache_dependencies_directory_name: PathBuf,
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum LockMode {
/// Verify that the lock file is up to date. This mode should be normally used on CI.
Locked,
/// Update the lock file if necessary.
Update,
/// Recreate the lock file from scratch.
Recreate,
}

impl Protofetch {
pub fn builder() -> ProtofetchBuilder {
ProtofetchBuilder::default()
Expand All @@ -32,9 +42,9 @@ impl Protofetch {
}

/// Fetches dependencies defined in the toml configuration file
pub fn fetch(&self, ignore_lock_file: bool) -> Result<(), Box<dyn Error>> {
pub fn fetch(&self, lock_mode: LockMode) -> Result<(), Box<dyn Error>> {
do_fetch(
ignore_lock_file,
lock_mode,
&self.cache,
&self.root,
&self.module_file_name,
Expand All @@ -44,9 +54,10 @@ impl Protofetch {
)
}

/// Creates a lock file based on the toml configuration file
pub fn lock(&self) -> Result<(), Box<dyn Error>> {
/// Creates, updates or verifies a lock file based on the toml configuration file
pub fn lock(&self, lock_mode: LockMode) -> Result<(), Box<dyn Error>> {
do_lock(
lock_mode,
&self.cache,
&self.root,
&self.module_file_name,
Expand Down
43 changes: 34 additions & 9 deletions src/cli/command_handlers.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use log::info;

use crate::{
api::LockMode,
cache::ProtofetchGitCache,
fetch,
model::{
protodep::ProtodepDescriptor,
protofetch::{lock::LockFile, Descriptor},
},
proto,
resolver::LockFileModuleResolver,
};
use std::{
error::Error,
Expand All @@ -18,7 +20,7 @@ const DEFAULT_OUTPUT_DIRECTORY_NAME: &str = "proto_src";

/// Handler to fetch command
pub fn do_fetch(
force_lock: bool,
lock_mode: LockMode,
cache: &ProtofetchGitCache,
root: &Path,
module_file_name: &Path,
Expand All @@ -28,12 +30,7 @@ pub fn do_fetch(
) -> Result<(), Box<dyn Error>> {
let module_descriptor = load_module_descriptor(root, module_file_name)?;

let lock_file_path = root.join(lock_file_name);
let lockfile = if force_lock || !lock_file_path.exists() {
do_lock(cache, root, module_file_name, lock_file_name)?
} else {
LockFile::from_file(&lock_file_path)?
};
let lockfile = do_lock(lock_mode, cache, root, module_file_name, lock_file_name)?;

let cache_dependencies_directory_path = cache.location.join(cache_dependencies_directory_name);
let output_directory_name = output_directory_name
Expand All @@ -55,15 +52,43 @@ pub fn do_fetch(
/// Loads dependency descriptor from protofetch toml or protodep toml
/// Generates a lock file based on the protofetch.toml
pub fn do_lock(
lock_mode: LockMode,
cache: &ProtofetchGitCache,
root: &Path,
module_file_name: &Path,
lock_file_name: &Path,
) -> Result<LockFile, Box<dyn Error>> {
let module_descriptor = load_module_descriptor(root, module_file_name)?;

log::debug!("Generating lockfile...");
let lockfile = fetch::lock(&module_descriptor, cache)?;
let lock_file_path = root.join(lock_file_name);

let lockfile = match (lock_mode, lock_file_path.exists()) {
(LockMode::Locked, false) => return Err("Lock file does not exist".into()),

(LockMode::Locked, true) => {
let lockfile = LockFile::from_file(&lock_file_path)?;
let resolver = LockFileModuleResolver::new(cache, lockfile, true);
log::debug!("Verifying lockfile...");
fetch::lock(&module_descriptor, &resolver)?
}

(LockMode::Update, false) => {
log::debug!("Generating lockfile...");
fetch::lock(&module_descriptor, &cache)?
}

(LockMode::Update, true) => {
let lockfile = LockFile::from_file(&lock_file_path)?;
let resolver = LockFileModuleResolver::new(cache, lockfile, false);
log::debug!("Updating lockfile...");
fetch::lock(&module_descriptor, &resolver)?
}

(LockMode::Recreate, _) => {
log::debug!("Generating lockfile...");
fetch::lock(&module_descriptor, &cache)?
}
};

log::debug!("Generated lockfile: {:?}", lockfile);
let value_toml = toml::Value::try_from(&lockfile)?;
Expand Down
40 changes: 22 additions & 18 deletions src/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
cache::{CacheError, RepositoryCache},
model::protofetch::{
lock::{LockFile, LockedDependency},
Dependency, DependencyName, Descriptor, RevisionSpecification,
Dependency, DependencyName, Descriptor,
},
proto_repository::ProtoRepository,
resolver::ModuleResolver,
Expand Down Expand Up @@ -42,7 +42,7 @@ pub fn lock(
) -> Result<LockFile, FetchError> {
fn go(
resolver: &impl ModuleResolver,
resolved: &mut BTreeMap<DependencyName, (RevisionSpecification, LockedDependency)>,
resolved: &mut BTreeMap<DependencyName, LockedDependency>,
dependencies: &[Dependency],
) -> Result<(), FetchError> {
let mut children = Vec::new();
Expand All @@ -68,29 +68,27 @@ pub fn lock(
name: dependency.name.clone(),
commit_hash: resolved_module.commit_hash,
coordinate: dependency.coordinate.clone(),
specification: dependency.specification.clone(),
dependencies,
rules: dependency.rules.clone(),
};

resolved.insert(
dependency.name.clone(),
(dependency.specification.clone(), locked),
);
resolved.insert(dependency.name.clone(), locked);
children.append(&mut resolved_module.descriptor.dependencies);
}
Some((resolved_specification, resolved)) => {
Some(resolved) => {
if resolved.coordinate != dependency.coordinate {
log::warn!(
"discarded {} in favor of {} for {}",
dependency.coordinate,
resolved.coordinate,
&dependency.name.value
);
} else if resolved_specification != &dependency.specification {
} else if resolved.specification != dependency.specification {
log::warn!(
"discarded {} in favor of {} for {}",
dependency.specification,
resolved_specification,
resolved.specification,
&dependency.name.value
);
}
Expand All @@ -111,10 +109,7 @@ pub fn lock(

Ok(LockFile {
module_name: descriptor.name.clone(),
dependencies: resolved
.into_values()
.map(|(_, dependency)| dependency)
.collect(),
dependencies: resolved.into_values().collect(),
})
}

Expand Down Expand Up @@ -229,12 +224,21 @@ mod tests {
}
}

fn locked_dependency(name: &str, commit_hash: &str, dependencies: &[&str]) -> LockedDependency {
fn locked_dependency(
name: &str,
revision: &str,
commit_hash: &str,
dependencies: &[&str],
) -> LockedDependency {
LockedDependency {
name: DependencyName {
value: name.to_owned(),
},
coordinate: coordinate(name),
specification: RevisionSpecification {
revision: Revision::pinned(revision),
branch: None,
},
rules: Rules::default(),
commit_hash: commit_hash.to_owned(),
dependencies: dependencies
Expand Down Expand Up @@ -289,8 +293,8 @@ mod tests {
LockFile {
module_name: "root".to_owned(),
dependencies: vec![
locked_dependency("bar", "c2", &[]),
locked_dependency("foo", "c1", &["bar"])
locked_dependency("bar", "2.0.0", "c2", &[]),
locked_dependency("foo", "1.0.0", "c1", &["bar"])
]
}
)
Expand Down Expand Up @@ -350,8 +354,8 @@ mod tests {
LockFile {
module_name: "root".to_owned(),
dependencies: vec![
locked_dependency("bar", "c3", &[]),
locked_dependency("foo", "c1", &["bar"]),
locked_dependency("bar", "1.0.0", "c3", &[]),
locked_dependency("foo", "1.0.0", "c1", &["bar"]),
]
}
)
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ mod proto;
mod proto_repository;
mod resolver;

pub use api::{Protofetch, ProtofetchBuilder};
pub use api::{LockMode, Protofetch, ProtofetchBuilder};
24 changes: 20 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use clap::Parser;
use env_logger::Target;

use log::warn;
use protofetch::Protofetch;
use protofetch::{LockMode, Protofetch};

/// Dependency management tool for Protocol Buffers files.
#[derive(Debug, Parser)]
Expand Down Expand Up @@ -37,8 +37,11 @@ pub struct CliArgs {
pub enum Command {
/// Fetches protodep dependencies defined in the toml configuration file
Fetch {
#[clap(short, long)]
/// reqiure dependencies to match the lock file
#[clap(long)]
locked: bool,
/// forces re-creation of lock file
#[clap(short, long, hide(true))]
force_lock: bool,
/// name of the dependencies repo checkout directory
/// this is a relative path within cache folder
Expand All @@ -47,6 +50,8 @@ pub enum Command {
},
/// Creates a lock file based on toml configuration file
Lock,
/// Updates the lock file
Update,
/// Creates an init protofetch setup in provided directory and name
Init {
#[clap(default_value = ".")]
Expand Down Expand Up @@ -124,6 +129,7 @@ fn run() -> Result<(), Box<dyn Error>> {

match cli_args.cmd {
Command::Fetch {
locked,
force_lock,
repo_output_directory,
} => {
Expand All @@ -133,9 +139,19 @@ fn run() -> Result<(), Box<dyn Error>> {
protofetch = protofetch.cache_dependencies_directory_name(repo_output_directory);
}

protofetch.try_build()?.fetch(force_lock)
let lock_mode = if force_lock {
warn!("Specifying --force-lock is deprecated, please use \"protofetch update\" instead.");
LockMode::Recreate
} else if locked {
LockMode::Locked
} else {
LockMode::Update
};

protofetch.try_build()?.fetch(lock_mode)
}
Command::Lock => protofetch.try_build()?.lock(),
Command::Lock => protofetch.try_build()?.lock(LockMode::Update),
Command::Update => protofetch.try_build()?.lock(LockMode::Recreate),
Command::Init { directory, name } => protofetch.root(directory).try_build()?.init(name),
Command::Migrate { directory, name } => protofetch
.root(&directory)
Expand Down
63 changes: 35 additions & 28 deletions src/model/protofetch/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,49 +34,56 @@ pub struct LockedDependency {
pub name: DependencyName,
pub commit_hash: String,
pub coordinate: Coordinate,
// default is needed for backwards compatibility with the existing lock files
#[serde(default, skip_serializing_if = "RevisionSpecification::is_default")]
pub specification: RevisionSpecification,
#[serde(skip_serializing_if = "BTreeSet::is_empty", default)]
pub dependencies: BTreeSet<DependencyName>,
pub rules: Rules,
}

#[cfg(test)]
mod tests {
use crate::model::protofetch::{AllowPolicies, DenyPolicies, FilePolicy, Protocol};
use crate::model::protofetch::{AllowPolicies, DenyPolicies, FilePolicy, Protocol, Revision};

use super::*;
use pretty_assertions::assert_eq;

#[test]
fn load_lock_file() {
let dependencies = vec![
LockedDependency {
name: DependencyName::new("dep1".to_string()),
commit_hash: "hash1".to_string(),
coordinate: Coordinate::from_url("example.com/org/dep1", Protocol::Https).unwrap(),
specification: RevisionSpecification {
revision: Revision::pinned("1.0.0"),
branch: Some("main".to_owned()),
},
dependencies: BTreeSet::from([DependencyName::new("dep2".to_string())]),
rules: Rules::new(
true,
false,
BTreeSet::new(),
AllowPolicies::new(BTreeSet::from([FilePolicy::try_from_str(
"/proto/example.proto",
)
.unwrap()])),
DenyPolicies::default(),
),
},
LockedDependency {
name: DependencyName::new("dep2".to_string()),
commit_hash: "hash2".to_string(),
coordinate: Coordinate::from_url("example.com/org/dep2", Protocol::Https).unwrap(),
specification: RevisionSpecification::default(),
dependencies: BTreeSet::new(),
rules: Rules::default(),
},
];
let lock_file = LockFile {
module_name: "test".to_string(),
dependencies: vec![
LockedDependency {
name: DependencyName::new("dep1".to_string()),
commit_hash: "hash1".to_string(),
coordinate: Coordinate::from_url("example.com/org/dep1", Protocol::Https)
.unwrap(),
dependencies: BTreeSet::from([DependencyName::new("dep2".to_string())]),
rules: Rules::new(
true,
false,
BTreeSet::new(),
AllowPolicies::new(BTreeSet::from([FilePolicy::try_from_str(
"/proto/example.proto",
)
.unwrap()])),
DenyPolicies::default(),
),
},
LockedDependency {
name: DependencyName::new("dep2".to_string()),
commit_hash: "hash2".to_string(),
coordinate: Coordinate::from_url("example.com/org/dep2", Protocol::Https)
.unwrap(),
dependencies: BTreeSet::new(),
rules: Rules::default(),
},
],
dependencies,
};
let value_toml = toml::Value::try_from(&lock_file).unwrap();
let string_fmt = toml::to_string_pretty(&value_toml).unwrap();
Expand Down
Loading