Skip to content

Commit

Permalink
Update the lock file when necessary
Browse files Browse the repository at this point in the history
  • Loading branch information
rtimush committed Oct 30, 2023
1 parent 98ff32e commit 4ae4f0d
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 18 deletions.
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
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
66 changes: 66 additions & 0 deletions src/resolver/lock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use anyhow::bail;
use log::debug;

use crate::model::protofetch::{lock::LockFile, Coordinate, DependencyName, RevisionSpecification};

use super::{ModuleResolver, ResolvedModule};

pub struct LockFileModuleResolver<R> {
inner: R,
lock_file: LockFile,
locked: bool,
}

impl<R> LockFileModuleResolver<R> {
pub fn new(inner: R, lock_file: LockFile, locked: bool) -> Self {
Self {
inner,
lock_file,
locked,
}
}
}

impl<R> ModuleResolver for LockFileModuleResolver<R>
where
R: ModuleResolver,
{
fn resolve(
&self,
coordinate: &Coordinate,
specification: &RevisionSpecification,
name: &DependencyName,
) -> anyhow::Result<ResolvedModule> {
let dependency = self.lock_file.dependencies.iter().find(|dependency| {
&dependency.coordinate == coordinate && &dependency.specification == specification
});
match dependency {
Some(dependency) => {
debug!(
"Dependency {} {} found in the lock file with commit {}",
coordinate, specification, dependency.commit_hash
);
let commit_hash = dependency.commit_hash.clone();
let resolved = self.inner.resolve(coordinate, specification, name)?;
if resolved.commit_hash != commit_hash {
bail!("Commit hash of {} {} changed: the lock file specifies {}, but the actual commit hash is {}", coordinate, specification, commit_hash, resolved.commit_hash);
}
Ok(resolved)
}
None if self.locked => {
bail!(
"No entry for {} {} in the lock file",
coordinate,
specification
);
}
None => {
debug!(
"Dependency {} {} not found in the lock file",
coordinate, specification
);
self.inner.resolve(coordinate, specification, name)
}
}
}
}
17 changes: 17 additions & 0 deletions src/resolver/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
mod git;
mod lock;

use crate::model::protofetch::{Coordinate, DependencyName, Descriptor, RevisionSpecification};

pub use lock::LockFileModuleResolver;

pub trait ModuleResolver {
fn resolve(
&self,
Expand All @@ -16,3 +19,17 @@ pub struct ResolvedModule {
pub commit_hash: String,
pub descriptor: Descriptor,
}

impl<T> ModuleResolver for &T
where
T: ModuleResolver,
{
fn resolve(
&self,
coordinate: &Coordinate,
specification: &RevisionSpecification,
name: &DependencyName,
) -> anyhow::Result<ResolvedModule> {
T::resolve(self, coordinate, specification, name)
}
}

0 comments on commit 4ae4f0d

Please sign in to comment.