mirror of
https://github.com/pesde-pkg/pesde.git
synced 2024-12-12 11:00:36 +00:00
feat: proper update command
This commit is contained in:
parent
c08dfb9965
commit
702153d81b
8 changed files with 245 additions and 118 deletions
|
@ -19,8 +19,8 @@ fn script_contents(path: &Path) -> String {
|
|||
r#"local process = require("@lune/process")
|
||||
local home_dir = if process.os == "windows" then process.env.userprofile else process.env.HOME
|
||||
|
||||
require(home_dir .. "/{HOME_DIR}/scripts/{}"#,
|
||||
path.display()
|
||||
require(home_dir .. {:?})"#,
|
||||
format!("/{HOME_DIR}/scripts/{}", path.display())
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,25 +1,30 @@
|
|||
use crate::cli::{bin_dir, files::make_executable};
|
||||
use crate::cli::{
|
||||
bin_dir, download_graph, files::make_executable, run_on_workspace_members, up_to_date_lockfile,
|
||||
};
|
||||
use anyhow::Context;
|
||||
use clap::Args;
|
||||
use colored::{ColoredString, Colorize};
|
||||
use indicatif::MultiProgress;
|
||||
use pesde::{lockfile::Lockfile, manifest::target::TargetKind, Project, MANIFEST_FILE_NAME};
|
||||
use relative_path::RelativePathBuf;
|
||||
use std::{
|
||||
collections::{BTreeSet, HashSet},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
use pesde::{
|
||||
lockfile::Lockfile,
|
||||
manifest::{target::TargetKind, DependencyType},
|
||||
Project, MANIFEST_FILE_NAME,
|
||||
};
|
||||
use std::collections::{BTreeSet, HashSet};
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[derive(Debug, Args, Copy, Clone)]
|
||||
pub struct InstallCommand {
|
||||
/// The amount of threads to use for downloading
|
||||
#[arg(short, long, default_value_t = 6, value_parser = clap::value_parser!(u64).range(1..=128))]
|
||||
threads: u64,
|
||||
|
||||
/// Whether to ignore the lockfile, refreshing it
|
||||
#[arg(short, long)]
|
||||
pub unlocked: bool,
|
||||
/// Whether to error on changes in the lockfile
|
||||
#[arg(long)]
|
||||
locked: bool,
|
||||
|
||||
/// Whether to not install dev dependencies
|
||||
#[arg(long)]
|
||||
prod: bool,
|
||||
}
|
||||
|
||||
fn bin_link_file(alias: &str) -> String {
|
||||
|
@ -92,8 +97,16 @@ impl InstallCommand {
|
|||
.deser_manifest()
|
||||
.context("failed to read manifest")?;
|
||||
|
||||
let lockfile = if self.unlocked {
|
||||
None
|
||||
let lockfile = if self.locked {
|
||||
match up_to_date_lockfile(&project)? {
|
||||
None => {
|
||||
anyhow::bail!(
|
||||
"lockfile is out of sync, run `{} install` to update it",
|
||||
env!("CARGO_BIN_NAME")
|
||||
);
|
||||
}
|
||||
file => file,
|
||||
}
|
||||
} else {
|
||||
match project.deser_lockfile() {
|
||||
Ok(lockfile) => {
|
||||
|
@ -166,51 +179,45 @@ impl InstallCommand {
|
|||
.dependency_graph(old_graph.as_ref(), &mut refreshed_sources)
|
||||
.context("failed to build dependency graph")?;
|
||||
|
||||
let bar = multi.add(
|
||||
indicatif::ProgressBar::new(graph.values().map(|versions| versions.len() as u64).sum())
|
||||
.with_style(
|
||||
indicatif::ProgressStyle::default_bar().template(
|
||||
"{msg} {bar:40.208/166} {pos}/{len} {percent}% {elapsed_precise}",
|
||||
)?,
|
||||
)
|
||||
.with_message(format!("{} 📥 downloading dependencies", job(3))),
|
||||
);
|
||||
bar.enable_steady_tick(Duration::from_millis(100));
|
||||
let downloaded_graph = download_graph(
|
||||
&project,
|
||||
&mut refreshed_sources,
|
||||
&graph,
|
||||
&multi,
|
||||
&reqwest,
|
||||
self.threads as usize,
|
||||
self.prod,
|
||||
true,
|
||||
format!("{} 📥 downloading dependencies", job(3)),
|
||||
format!("{} 📥 downloaded dependencies", job(3)),
|
||||
)?;
|
||||
|
||||
let (rx, downloaded_graph) = project
|
||||
.download_graph(
|
||||
&graph,
|
||||
&mut refreshed_sources,
|
||||
&reqwest,
|
||||
self.threads as usize,
|
||||
)
|
||||
.context("failed to download dependencies")?;
|
||||
|
||||
while let Ok(result) = rx.recv() {
|
||||
bar.inc(1);
|
||||
|
||||
match result {
|
||||
Ok(()) => {}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
bar.finish_with_message(format!("{} 📥 downloaded dependencies", job(3),));
|
||||
|
||||
let downloaded_graph = Arc::into_inner(downloaded_graph)
|
||||
.unwrap()
|
||||
.into_inner()
|
||||
.unwrap();
|
||||
let filtered_graph = if self.prod {
|
||||
downloaded_graph
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|(n, v)| {
|
||||
(
|
||||
n,
|
||||
v.into_iter()
|
||||
.filter(|(_, n)| n.node.ty != DependencyType::Dev)
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
downloaded_graph.clone()
|
||||
};
|
||||
|
||||
println!("{} 🗺️ linking dependencies", job(4));
|
||||
|
||||
project
|
||||
.link_dependencies(&downloaded_graph)
|
||||
.link_dependencies(&filtered_graph)
|
||||
.context("failed to link dependencies")?;
|
||||
|
||||
let bin_folder = bin_dir()?;
|
||||
|
||||
for versions in downloaded_graph.values() {
|
||||
for versions in filtered_graph.values() {
|
||||
for node in versions.values() {
|
||||
if node.target.bin_path().is_none() {
|
||||
continue;
|
||||
|
@ -248,7 +255,7 @@ impl InstallCommand {
|
|||
println!("{} 🩹 applying patches", job(5));
|
||||
|
||||
project
|
||||
.apply_patches(&downloaded_graph)
|
||||
.apply_patches(&filtered_graph)
|
||||
.context("failed to apply patches")?;
|
||||
}
|
||||
|
||||
|
@ -260,48 +267,12 @@ impl InstallCommand {
|
|||
version: manifest.version,
|
||||
target: manifest.target.kind(),
|
||||
overrides: manifest.overrides,
|
||||
workspace: match project.workspace_dir() {
|
||||
Some(_) => {
|
||||
// this might seem counterintuitive, but remember that the workspace
|
||||
// is the package_dir when the user isn't in a member package
|
||||
Default::default()
|
||||
}
|
||||
None => project
|
||||
.workspace_members(project.package_dir())
|
||||
.context("failed to get workspace members")?
|
||||
.into_iter()
|
||||
.map(|(path, manifest)| {
|
||||
(
|
||||
manifest.name,
|
||||
RelativePathBuf::from_path(
|
||||
path.strip_prefix(project.package_dir()).unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
})
|
||||
.map(|(name, path)| {
|
||||
InstallCommand {
|
||||
threads: self.threads,
|
||||
unlocked: self.unlocked,
|
||||
}
|
||||
.run(
|
||||
Project::new(
|
||||
path.to_path(project.package_dir()),
|
||||
Some(project.package_dir()),
|
||||
project.data_dir(),
|
||||
project.cas_dir(),
|
||||
project.auth_config().clone(),
|
||||
),
|
||||
multi.clone(),
|
||||
reqwest.clone(),
|
||||
)
|
||||
.map(|_| (name, path))
|
||||
})
|
||||
.collect::<Result<_, _>>()
|
||||
.context("failed to install workspace member's dependencies")?,
|
||||
},
|
||||
|
||||
graph: downloaded_graph,
|
||||
|
||||
workspace: run_on_workspace_members(&project, |project| {
|
||||
self.run(project, multi.clone(), reqwest.clone())
|
||||
})?,
|
||||
})
|
||||
.context("failed to write lockfile")?;
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ mod publish;
|
|||
mod run;
|
||||
mod self_install;
|
||||
mod self_upgrade;
|
||||
mod update;
|
||||
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
pub enum Subcommand {
|
||||
|
@ -57,8 +58,8 @@ pub enum Subcommand {
|
|||
/// Adds a dependency to the project
|
||||
Add(add::AddCommand),
|
||||
|
||||
/// Updates the project's lockfile. note: this command is just an alias for `install --unlocked`
|
||||
Update(install::InstallCommand),
|
||||
/// Updates the project's lockfile. Run install to apply changes
|
||||
Update(update::UpdateCommand),
|
||||
|
||||
/// Checks for outdated dependencies
|
||||
Outdated(outdated::OutdatedCommand),
|
||||
|
@ -90,10 +91,7 @@ impl Subcommand {
|
|||
Subcommand::PatchCommit(patch_commit) => patch_commit.run(project),
|
||||
Subcommand::SelfUpgrade(self_upgrade) => self_upgrade.run(reqwest),
|
||||
Subcommand::Add(add) => add.run(project),
|
||||
Subcommand::Update(mut update) => {
|
||||
update.unlocked = true;
|
||||
update.run(project, multi, reqwest)
|
||||
}
|
||||
Subcommand::Update(update) => update.run(project, multi, reqwest),
|
||||
Subcommand::Outdated(outdated) => outdated.run(project),
|
||||
#[cfg(any(feature = "lune", feature = "luau"))]
|
||||
Subcommand::Execute(execute) => execute.run(project, reqwest),
|
||||
|
|
60
src/cli/commands/update.rs
Normal file
60
src/cli/commands/update.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
use crate::cli::{download_graph, run_on_workspace_members};
|
||||
use anyhow::Context;
|
||||
use clap::Args;
|
||||
use indicatif::MultiProgress;
|
||||
use pesde::{lockfile::Lockfile, Project};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Debug, Args, Copy, Clone)]
|
||||
pub struct UpdateCommand {
|
||||
/// The amount of threads to use for downloading
|
||||
#[arg(short, long, default_value_t = 6, value_parser = clap::value_parser!(u64).range(1..=128))]
|
||||
threads: u64,
|
||||
}
|
||||
|
||||
impl UpdateCommand {
|
||||
pub fn run(
|
||||
self,
|
||||
project: Project,
|
||||
multi: MultiProgress,
|
||||
reqwest: reqwest::blocking::Client,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut refreshed_sources = HashSet::new();
|
||||
|
||||
let manifest = project
|
||||
.deser_manifest()
|
||||
.context("failed to read manifest")?;
|
||||
|
||||
let graph = project
|
||||
.dependency_graph(None, &mut refreshed_sources)
|
||||
.context("failed to build dependency graph")?;
|
||||
|
||||
project
|
||||
.write_lockfile(Lockfile {
|
||||
name: manifest.name,
|
||||
version: manifest.version,
|
||||
target: manifest.target.kind(),
|
||||
overrides: manifest.overrides,
|
||||
|
||||
graph: download_graph(
|
||||
&project,
|
||||
&mut refreshed_sources,
|
||||
&graph,
|
||||
&multi,
|
||||
&reqwest,
|
||||
self.threads as usize,
|
||||
false,
|
||||
false,
|
||||
"📥 downloading dependencies".to_string(),
|
||||
"📥 downloaded dependencies".to_string(),
|
||||
)?,
|
||||
|
||||
workspace: run_on_workspace_members(&project, |project| {
|
||||
self.run(project, multi.clone(), reqwest.clone())
|
||||
})?,
|
||||
})
|
||||
.context("failed to write lockfile")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
100
src/cli/mod.rs
100
src/cli/mod.rs
|
@ -1,17 +1,22 @@
|
|||
use crate::cli::auth::get_token;
|
||||
use anyhow::Context;
|
||||
use gix::bstr::BStr;
|
||||
use indicatif::MultiProgress;
|
||||
use pesde::{
|
||||
lockfile::{DownloadedGraph, Lockfile},
|
||||
lockfile::{DependencyGraph, DownloadedGraph, Lockfile},
|
||||
names::{PackageName, PackageNames},
|
||||
source::{version_id::VersionId, workspace::specifier::VersionType},
|
||||
source::{version_id::VersionId, workspace::specifier::VersionType, PackageSources},
|
||||
Project,
|
||||
};
|
||||
use relative_path::RelativePathBuf;
|
||||
use serde::{ser::SerializeMap, Deserialize, Deserializer, Serializer};
|
||||
use std::{
|
||||
collections::{BTreeMap, HashSet},
|
||||
fs::create_dir_all,
|
||||
path::PathBuf,
|
||||
str::FromStr,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
pub mod auth;
|
||||
|
@ -23,13 +28,13 @@ pub mod version;
|
|||
|
||||
pub const HOME_DIR: &str = concat!(".", env!("CARGO_PKG_NAME"));
|
||||
|
||||
pub fn home_dir() -> anyhow::Result<std::path::PathBuf> {
|
||||
pub fn home_dir() -> anyhow::Result<PathBuf> {
|
||||
Ok(dirs::home_dir()
|
||||
.context("failed to get home directory")?
|
||||
.join(HOME_DIR))
|
||||
}
|
||||
|
||||
pub fn bin_dir() -> anyhow::Result<std::path::PathBuf> {
|
||||
pub fn bin_dir() -> anyhow::Result<PathBuf> {
|
||||
let bin_dir = home_dir()?.join("bin");
|
||||
create_dir_all(&bin_dir).context("failed to create bin folder")?;
|
||||
Ok(bin_dir)
|
||||
|
@ -203,3 +208,90 @@ pub fn deserialize_string_url_map<'de, D: Deserializer<'de>>(
|
|||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn download_graph(
|
||||
project: &Project,
|
||||
refreshed_sources: &mut HashSet<PackageSources>,
|
||||
graph: &DependencyGraph,
|
||||
multi: &MultiProgress,
|
||||
reqwest: &reqwest::blocking::Client,
|
||||
threads: usize,
|
||||
prod: bool,
|
||||
write: bool,
|
||||
progress_msg: String,
|
||||
finish_msg: String,
|
||||
) -> anyhow::Result<DownloadedGraph> {
|
||||
let bar = multi.add(
|
||||
indicatif::ProgressBar::new(graph.values().map(|versions| versions.len() as u64).sum())
|
||||
.with_style(
|
||||
indicatif::ProgressStyle::default_bar()
|
||||
.template("{msg} {bar:40.208/166} {pos}/{len} {percent}% {elapsed_precise}")?,
|
||||
)
|
||||
.with_message(progress_msg),
|
||||
);
|
||||
bar.enable_steady_tick(Duration::from_millis(100));
|
||||
|
||||
let (rx, downloaded_graph) = project
|
||||
.download_graph(graph, refreshed_sources, reqwest, threads, prod, write)
|
||||
.context("failed to download dependencies")?;
|
||||
|
||||
while let Ok(result) = rx.recv() {
|
||||
bar.inc(1);
|
||||
|
||||
match result {
|
||||
Ok(()) => {}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
bar.finish_with_message(finish_msg);
|
||||
|
||||
Ok(Arc::into_inner(downloaded_graph)
|
||||
.unwrap()
|
||||
.into_inner()
|
||||
.unwrap())
|
||||
}
|
||||
|
||||
pub fn shift_project_dir(project: &Project, pkg_dir: PathBuf) -> Project {
|
||||
Project::new(
|
||||
pkg_dir,
|
||||
Some(project.package_dir()),
|
||||
project.data_dir(),
|
||||
project.cas_dir(),
|
||||
project.auth_config().clone(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn run_on_workspace_members(
|
||||
project: &Project,
|
||||
f: impl Fn(Project) -> anyhow::Result<()>,
|
||||
) -> anyhow::Result<BTreeMap<PackageName, RelativePathBuf>> {
|
||||
Ok(match project.workspace_dir() {
|
||||
Some(_) => {
|
||||
// this might seem counterintuitive, but remember that the workspace
|
||||
// is the package_dir when the user isn't in a member package
|
||||
Default::default()
|
||||
}
|
||||
None => project
|
||||
.workspace_members(project.package_dir())
|
||||
.context("failed to get workspace members")?
|
||||
.into_iter()
|
||||
.map(|(path, manifest)| {
|
||||
(
|
||||
manifest.name,
|
||||
RelativePathBuf::from_path(path.strip_prefix(project.package_dir()).unwrap())
|
||||
.unwrap(),
|
||||
)
|
||||
})
|
||||
.map(|(name, path)| {
|
||||
f(shift_project_dir(
|
||||
project,
|
||||
path.to_path(project.package_dir()),
|
||||
))
|
||||
.map(|_| (name, path))
|
||||
})
|
||||
.collect::<Result<_, _>>()
|
||||
.context("failed to install workspace member's dependencies")?,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{
|
||||
lockfile::{DependencyGraph, DownloadedDependencyGraphNode, DownloadedGraph},
|
||||
manifest::DependencyType,
|
||||
source::{
|
||||
traits::{PackageRef, PackageSource},
|
||||
PackageSources,
|
||||
|
@ -27,6 +28,8 @@ impl Project {
|
|||
refreshed_sources: &mut HashSet<PackageSources>,
|
||||
reqwest: &reqwest::blocking::Client,
|
||||
threads: usize,
|
||||
prod: bool,
|
||||
write: bool,
|
||||
) -> Result<MultithreadDownloadJob, errors::DownloadGraphError> {
|
||||
let manifest = self.deser_manifest()?;
|
||||
let downloaded_graph: MultithreadedGraph = Arc::new(Mutex::new(Default::default()));
|
||||
|
@ -83,14 +86,20 @@ impl Project {
|
|||
|
||||
log::debug!("downloaded {name}@{version_id}");
|
||||
|
||||
match fs.write_to(container_folder, project.cas_dir(), true) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
tx.send(Err(errors::DownloadGraphError::WriteFailed(e)))
|
||||
.unwrap();
|
||||
return;
|
||||
if write {
|
||||
if !prod || node.ty != DependencyType::Dev {
|
||||
match fs.write_to(container_folder, project.cas_dir(), true) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
tx.send(Err(errors::DownloadGraphError::WriteFailed(e)))
|
||||
.unwrap();
|
||||
return;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
log::debug!("skipping writing {name}@{version_id} to disk, dev dependency in prod mode");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let mut downloaded_graph = downloaded_graph.lock().unwrap();
|
||||
downloaded_graph
|
||||
|
|
|
@ -85,7 +85,10 @@ impl Project {
|
|||
.get(&name)
|
||||
.and_then(|versions| versions.get(&version_id))
|
||||
else {
|
||||
return Err(errors::ApplyPatchesError::PackageNotFound(name, version_id));
|
||||
log::warn!(
|
||||
"patch for {name}@{version_id} not applied because it is not in the graph"
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
let container_folder = node.node.container_folder(
|
||||
|
@ -155,7 +158,6 @@ impl Project {
|
|||
pub mod errors {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::{names::PackageNames, source::version_id::VersionId};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Errors that can occur when applying patches
|
||||
|
@ -177,9 +179,5 @@ pub mod errors {
|
|||
/// Error removing the .git directory
|
||||
#[error("error removing .git directory")]
|
||||
GitDirectoryRemovalError(PathBuf, #[source] std::io::Error),
|
||||
|
||||
/// Package not found in the graph
|
||||
#[error("package {0}@{1} not found in graph")]
|
||||
PackageNotFound(PackageNames, VersionId),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ use crate::{
|
|||
names::PackageNames,
|
||||
source::{
|
||||
pesde::PesdePackageSource,
|
||||
refs::PackageRefs,
|
||||
specifiers::DependencySpecifiers,
|
||||
traits::{PackageRef, PackageSource},
|
||||
version_id::VersionId,
|
||||
|
@ -244,8 +243,8 @@ impl Project {
|
|||
target_version_id
|
||||
);
|
||||
|
||||
if matches!(already_resolved.pkg_ref, PackageRefs::Git(_))
|
||||
!= matches!(pkg_ref, PackageRefs::Git(_))
|
||||
if std::mem::discriminant(&already_resolved.pkg_ref)
|
||||
!= std::mem::discriminant(pkg_ref)
|
||||
{
|
||||
log::warn!(
|
||||
"resolved package {name}@{target_version_id} has a different source than the previously resolved one, this may cause issues",
|
||||
|
|
Loading…
Reference in a new issue