mirror of
https://github.com/pesde-pkg/pesde.git
synced 2024-12-12 11:00:36 +00:00
feat: support adding workspace packages
This commit is contained in:
parent
5236be53fd
commit
71eacb8673
6 changed files with 168 additions and 99 deletions
|
@ -4,7 +4,7 @@ use anyhow::Context;
|
|||
use clap::Args;
|
||||
use semver::VersionReq;
|
||||
|
||||
use crate::cli::{config::read_config, NamedVersionable, VersionedPackageName};
|
||||
use crate::cli::{config::read_config, AnyPackageIdentifier, VersionedPackageName};
|
||||
use pesde::{
|
||||
manifest::target::TargetKind,
|
||||
names::PackageNames,
|
||||
|
@ -13,6 +13,7 @@ use pesde::{
|
|||
pesde::{specifier::PesdeDependencySpecifier, PesdePackageSource},
|
||||
specifiers::DependencySpecifiers,
|
||||
traits::PackageSource,
|
||||
workspace::WorkspacePackageSource,
|
||||
PackageSources,
|
||||
},
|
||||
Project, DEFAULT_INDEX_NAME,
|
||||
|
@ -22,7 +23,7 @@ use pesde::{
|
|||
pub struct AddCommand {
|
||||
/// The package name to add
|
||||
#[arg(index = 1)]
|
||||
name: NamedVersionable<VersionReq>,
|
||||
name: AnyPackageIdentifier<VersionReq>,
|
||||
|
||||
/// The index in which to search for the package
|
||||
#[arg(short, long)]
|
||||
|
@ -52,7 +53,7 @@ impl AddCommand {
|
|||
.context("failed to read manifest")?;
|
||||
|
||||
let (source, specifier) = match &self.name {
|
||||
NamedVersionable::PackageName(versioned) => match &versioned {
|
||||
AnyPackageIdentifier::PackageName(versioned) => match &versioned {
|
||||
VersionedPackageName(PackageNames::Pesde(name), version) => {
|
||||
let index = manifest
|
||||
.indices
|
||||
|
@ -103,7 +104,7 @@ impl AddCommand {
|
|||
(source, specifier)
|
||||
}
|
||||
},
|
||||
NamedVersionable::Url((url, rev)) => (
|
||||
AnyPackageIdentifier::Url((url, rev)) => (
|
||||
PackageSources::Git(GitPackageSource::new(url.clone())),
|
||||
DependencySpecifiers::Git(GitDependencySpecifier {
|
||||
repo: url.clone(),
|
||||
|
@ -111,6 +112,15 @@ impl AddCommand {
|
|||
path: None,
|
||||
}),
|
||||
),
|
||||
AnyPackageIdentifier::Workspace(VersionedPackageName(name, version)) => (
|
||||
PackageSources::Workspace(WorkspacePackageSource),
|
||||
DependencySpecifiers::Workspace(
|
||||
pesde::source::workspace::specifier::WorkspaceDependencySpecifier {
|
||||
name: name.clone(),
|
||||
version_type: version.unwrap_or_default(),
|
||||
},
|
||||
),
|
||||
),
|
||||
};
|
||||
source
|
||||
.refresh(&project)
|
||||
|
@ -141,15 +151,16 @@ impl AddCommand {
|
|||
"dependencies"
|
||||
};
|
||||
|
||||
let alias = self.alias.unwrap_or_else(|| match self.name {
|
||||
NamedVersionable::PackageName(versioned) => versioned.0.as_str().1.to_string(),
|
||||
NamedVersionable::Url((url, _)) => url
|
||||
let alias = self.alias.unwrap_or_else(|| match self.name.clone() {
|
||||
AnyPackageIdentifier::PackageName(versioned) => versioned.0.as_str().1.to_string(),
|
||||
AnyPackageIdentifier::Url((url, _)) => url
|
||||
.path
|
||||
.to_string()
|
||||
.split('/')
|
||||
.last()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or(url.path.to_string()),
|
||||
AnyPackageIdentifier::Workspace(versioned) => versioned.0.as_str().1.to_string(),
|
||||
});
|
||||
|
||||
let field = &mut manifest[dependency_key]
|
||||
|
@ -198,7 +209,19 @@ impl AddCommand {
|
|||
|
||||
println!("added git {}#{} to {}", spec.repo, spec.rev, dependency_key);
|
||||
}
|
||||
DependencySpecifiers::Workspace(_) => todo!(),
|
||||
DependencySpecifiers::Workspace(spec) => {
|
||||
field["workspace"] = toml_edit::value(spec.name.clone().to_string());
|
||||
if let AnyPackageIdentifier::Workspace(versioned) = self.name {
|
||||
if let Some(version) = versioned.1 {
|
||||
field["version"] = toml_edit::value(version.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
println!(
|
||||
"added workspace {}@{} to {}",
|
||||
spec.name, spec.version_type, dependency_key
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
project
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::cli::{bin_dir, files::make_executable, IsUpToDate};
|
||||
use crate::cli::{bin_dir, files::make_executable};
|
||||
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;
|
||||
|
@ -70,6 +71,15 @@ end
|
|||
)
|
||||
}
|
||||
|
||||
#[cfg(feature = "patches")]
|
||||
const JOBS: u8 = 6;
|
||||
#[cfg(not(feature = "patches"))]
|
||||
const JOBS: u8 = 5;
|
||||
|
||||
fn job(n: u8) -> ColoredString {
|
||||
format!("[{n}/{JOBS}]").dimmed().bold()
|
||||
}
|
||||
|
||||
impl InstallCommand {
|
||||
pub fn run(
|
||||
self,
|
||||
|
@ -85,12 +95,19 @@ impl InstallCommand {
|
|||
|
||||
let lockfile = if self.unlocked {
|
||||
None
|
||||
} else if project
|
||||
.is_up_to_date(false)
|
||||
.context("failed to check if project is up to date")?
|
||||
{
|
||||
} else {
|
||||
match project.deser_lockfile() {
|
||||
Ok(lockfile) => Some(lockfile),
|
||||
Ok(lockfile) => {
|
||||
if lockfile.overrides != manifest.overrides {
|
||||
log::debug!("overrides are different");
|
||||
None
|
||||
} else if lockfile.target != manifest.target.kind() {
|
||||
log::debug!("target kind is different");
|
||||
None
|
||||
} else {
|
||||
Some(lockfile)
|
||||
}
|
||||
}
|
||||
Err(pesde::errors::LockfileReadError::Io(e))
|
||||
if e.kind() == std::io::ErrorKind::NotFound =>
|
||||
{
|
||||
|
@ -98,10 +115,17 @@ impl InstallCommand {
|
|||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
println!(
|
||||
"\n{}\n",
|
||||
format!("[now installing {}]", manifest.name)
|
||||
.bold()
|
||||
.on_bright_black()
|
||||
);
|
||||
|
||||
println!("{} ❌ removing current package folders", job(1));
|
||||
|
||||
{
|
||||
let mut deleted_folders = HashSet::new();
|
||||
|
||||
|
@ -137,6 +161,8 @@ impl InstallCommand {
|
|||
.collect()
|
||||
});
|
||||
|
||||
println!("{} 📦 building dependency graph", job(2));
|
||||
|
||||
let graph = project
|
||||
.dependency_graph(old_graph.as_ref(), &mut refreshed_sources)
|
||||
.context("failed to build dependency graph")?;
|
||||
|
@ -148,7 +174,7 @@ impl InstallCommand {
|
|||
"{msg} {bar:40.208/166} {pos}/{len} {percent}% {elapsed_precise}",
|
||||
)?,
|
||||
)
|
||||
.with_message(format!("downloading dependencies of {}", manifest.name)),
|
||||
.with_message(format!("{} 📥 downloading dependencies", job(3))),
|
||||
);
|
||||
bar.enable_steady_tick(Duration::from_millis(100));
|
||||
|
||||
|
@ -170,25 +196,19 @@ impl InstallCommand {
|
|||
}
|
||||
}
|
||||
|
||||
bar.finish_with_message(format!(
|
||||
"finished downloading dependencies of {}",
|
||||
manifest.name
|
||||
));
|
||||
bar.finish_with_message(format!("{} 📥 downloaded dependencies", job(3),));
|
||||
|
||||
let downloaded_graph = Arc::into_inner(downloaded_graph)
|
||||
.unwrap()
|
||||
.into_inner()
|
||||
.unwrap();
|
||||
|
||||
println!("{} 🗺️ linking dependencies", job(4));
|
||||
|
||||
project
|
||||
.link_dependencies(&downloaded_graph)
|
||||
.context("failed to link dependencies")?;
|
||||
|
||||
#[cfg(feature = "patches")]
|
||||
project
|
||||
.apply_patches(&downloaded_graph)
|
||||
.context("failed to apply patches")?;
|
||||
|
||||
let bin_folder = bin_dir()?;
|
||||
|
||||
for versions in downloaded_graph.values() {
|
||||
|
@ -224,6 +244,17 @@ impl InstallCommand {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "patches")]
|
||||
{
|
||||
println!("{} 🩹 applying patches", job(5));
|
||||
|
||||
project
|
||||
.apply_patches(&downloaded_graph)
|
||||
.context("failed to apply patches")?;
|
||||
}
|
||||
|
||||
println!("{} 🧹 finishing up", job(JOBS));
|
||||
|
||||
project
|
||||
.write_lockfile(Lockfile {
|
||||
name: manifest.name,
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
use crate::cli::{IsUpToDate, VersionedPackageName};
|
||||
use crate::cli::{up_to_date_lockfile, VersionedPackageName};
|
||||
use anyhow::Context;
|
||||
use clap::Args;
|
||||
use colored::Colorize;
|
||||
use pesde::{
|
||||
patches::setup_patches_repo,
|
||||
source::traits::{PackageRef, PackageSource},
|
||||
source::{
|
||||
refs::PackageRefs,
|
||||
traits::{PackageRef, PackageSource},
|
||||
},
|
||||
Project, MANIFEST_FILE_NAME,
|
||||
};
|
||||
|
||||
|
@ -17,8 +20,8 @@ pub struct PatchCommand {
|
|||
|
||||
impl PatchCommand {
|
||||
pub fn run(self, project: Project, reqwest: reqwest::blocking::Client) -> anyhow::Result<()> {
|
||||
let graph = if project.is_up_to_date(true)? {
|
||||
project.deser_lockfile()?.graph
|
||||
let graph = if let Some(lockfile) = up_to_date_lockfile(&project)? {
|
||||
lockfile.graph
|
||||
} else {
|
||||
anyhow::bail!("outdated lockfile, please run the install command first")
|
||||
};
|
||||
|
@ -29,6 +32,11 @@ impl PatchCommand {
|
|||
.get(&name)
|
||||
.and_then(|versions| versions.get(&version_id))
|
||||
.context("package not found in graph")?;
|
||||
|
||||
if matches!(node.node.pkg_ref, PackageRefs::Workspace(_)) {
|
||||
anyhow::bail!("cannot patch a workspace package")
|
||||
}
|
||||
|
||||
let source = node.node.pkg_ref.source();
|
||||
|
||||
let directory = project
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::cli::IsUpToDate;
|
||||
use crate::cli::up_to_date_lockfile;
|
||||
use anyhow::Context;
|
||||
use clap::Args;
|
||||
use pesde::{names::PackageNames, patches::create_patch, source::version_id::VersionId, Project};
|
||||
|
@ -13,8 +13,8 @@ pub struct PatchCommitCommand {
|
|||
|
||||
impl PatchCommitCommand {
|
||||
pub fn run(self, project: Project) -> anyhow::Result<()> {
|
||||
let graph = if project.is_up_to_date(true)? {
|
||||
project.deser_lockfile()?.graph
|
||||
let graph = if let Some(lockfile) = up_to_date_lockfile(&project)? {
|
||||
lockfile.graph
|
||||
} else {
|
||||
anyhow::bail!("outdated lockfile, please run the install command first")
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ use anyhow::Context;
|
|||
use clap::Args;
|
||||
use relative_path::RelativePathBuf;
|
||||
|
||||
use crate::cli::IsUpToDate;
|
||||
use crate::cli::up_to_date_lockfile;
|
||||
use pesde::{
|
||||
names::{PackageName, PackageNames},
|
||||
source::traits::PackageRef,
|
||||
|
@ -49,8 +49,8 @@ impl RunCommand {
|
|||
};
|
||||
|
||||
if let Ok(pkg_name) = package_or_script.parse::<PackageName>() {
|
||||
let graph = if project.is_up_to_date(true)? {
|
||||
project.deser_lockfile()?.graph
|
||||
let graph = if let Some(lockfile) = up_to_date_lockfile(&project)? {
|
||||
lockfile.graph
|
||||
} else {
|
||||
anyhow::bail!("outdated lockfile, please run the install command first")
|
||||
};
|
||||
|
|
133
src/cli/mod.rs
133
src/cli/mod.rs
|
@ -1,7 +1,11 @@
|
|||
use crate::cli::auth::get_token;
|
||||
use anyhow::Context;
|
||||
use gix::bstr::BStr;
|
||||
use pesde::{
|
||||
lockfile::DownloadedGraph, names::PackageNames, source::version_id::VersionId, Project,
|
||||
lockfile::{DownloadedGraph, Lockfile},
|
||||
names::{PackageName, PackageNames},
|
||||
source::{version_id::VersionId, workspace::specifier::VersionType},
|
||||
Project,
|
||||
};
|
||||
use serde::{ser::SerializeMap, Deserialize, Deserializer, Serializer};
|
||||
use std::{
|
||||
|
@ -10,8 +14,6 @@ use std::{
|
|||
str::FromStr,
|
||||
};
|
||||
|
||||
use crate::cli::auth::get_token;
|
||||
|
||||
pub mod auth;
|
||||
pub mod commands;
|
||||
pub mod config;
|
||||
|
@ -33,62 +35,58 @@ pub fn bin_dir() -> anyhow::Result<std::path::PathBuf> {
|
|||
Ok(bin_dir)
|
||||
}
|
||||
|
||||
pub trait IsUpToDate {
|
||||
fn is_up_to_date(&self, strict: bool) -> anyhow::Result<bool>;
|
||||
}
|
||||
|
||||
impl IsUpToDate for Project {
|
||||
fn is_up_to_date(&self, strict: bool) -> anyhow::Result<bool> {
|
||||
let manifest = self.deser_manifest()?;
|
||||
let lockfile = match self.deser_lockfile() {
|
||||
Ok(lockfile) => lockfile,
|
||||
Err(pesde::errors::LockfileReadError::Io(e))
|
||||
if e.kind() == std::io::ErrorKind::NotFound =>
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
|
||||
if manifest.overrides != lockfile.overrides {
|
||||
log::debug!("overrides are different");
|
||||
return Ok(false);
|
||||
pub fn up_to_date_lockfile(project: &Project) -> anyhow::Result<Option<Lockfile>> {
|
||||
let manifest = project.deser_manifest()?;
|
||||
let lockfile = match project.deser_lockfile() {
|
||||
Ok(lockfile) => lockfile,
|
||||
Err(pesde::errors::LockfileReadError::Io(e))
|
||||
if e.kind() == std::io::ErrorKind::NotFound =>
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
|
||||
if manifest.target.kind() != lockfile.target {
|
||||
log::debug!("target kind is different");
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if !strict {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
if manifest.name != lockfile.name || manifest.version != lockfile.version {
|
||||
log::debug!("name or version is different");
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let specs = lockfile
|
||||
.graph
|
||||
.into_iter()
|
||||
.flat_map(|(_, versions)| versions)
|
||||
.filter_map(|(_, node)| match node.node.direct {
|
||||
Some((_, spec)) => Some((spec, node.node.ty)),
|
||||
None => None,
|
||||
})
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
let same_dependencies = manifest
|
||||
.all_dependencies()
|
||||
.context("failed to get all dependencies")?
|
||||
.iter()
|
||||
.all(|(_, (spec, ty))| specs.contains(&(spec.clone(), *ty)));
|
||||
|
||||
log::debug!("dependencies are the same: {same_dependencies}");
|
||||
|
||||
Ok(same_dependencies)
|
||||
if manifest.overrides != lockfile.overrides {
|
||||
log::debug!("overrides are different");
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if manifest.target.kind() != lockfile.target {
|
||||
log::debug!("target kind is different");
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if manifest.name != lockfile.name || manifest.version != lockfile.version {
|
||||
log::debug!("name or version is different");
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let specs = lockfile
|
||||
.graph
|
||||
.iter()
|
||||
.flat_map(|(_, versions)| versions)
|
||||
.filter_map(|(_, node)| {
|
||||
node.node
|
||||
.direct
|
||||
.as_ref()
|
||||
.map(|(_, spec)| (spec, node.node.ty))
|
||||
})
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
let same_dependencies = manifest
|
||||
.all_dependencies()
|
||||
.context("failed to get all dependencies")?
|
||||
.iter()
|
||||
.all(|(_, (spec, ty))| specs.contains(&(spec, *ty)));
|
||||
|
||||
log::debug!("dependencies are the same: {same_dependencies}");
|
||||
|
||||
Ok(if same_dependencies {
|
||||
Some(lockfile)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -143,28 +141,37 @@ impl VersionedPackageName {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum NamedVersionable<V: FromStr = VersionId, N: FromStr = PackageNames> {
|
||||
enum AnyPackageIdentifier<V: FromStr = VersionId, N: FromStr = PackageNames> {
|
||||
PackageName(VersionedPackageName<V, N>),
|
||||
Url((gix::Url, String)),
|
||||
Workspace(VersionedPackageName<VersionType, PackageName>),
|
||||
}
|
||||
|
||||
impl<V: FromStr<Err = E>, E: Into<anyhow::Error>, N: FromStr<Err = F>, F: Into<anyhow::Error>>
|
||||
FromStr for NamedVersionable<V, N>
|
||||
FromStr for AnyPackageIdentifier<V, N>
|
||||
{
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if s.contains("gh#") {
|
||||
let s = s.replacen("gh#", "https://github.com/", 1);
|
||||
if let Some(s) = s.strip_prefix("gh#") {
|
||||
let s = format!("https://github.com/{s}");
|
||||
let (repo, rev) = s.split_once('#').unwrap();
|
||||
|
||||
Ok(NamedVersionable::Url((repo.try_into()?, rev.to_string())))
|
||||
Ok(AnyPackageIdentifier::Url((
|
||||
repo.try_into()?,
|
||||
rev.to_string(),
|
||||
)))
|
||||
} else if let Some(rest) = s.strip_prefix("workspace:") {
|
||||
Ok(AnyPackageIdentifier::Workspace(rest.parse()?))
|
||||
} else if s.contains(':') {
|
||||
let (url, rev) = s.split_once('#').unwrap();
|
||||
|
||||
Ok(NamedVersionable::Url((url.try_into()?, rev.to_string())))
|
||||
Ok(AnyPackageIdentifier::Url((
|
||||
url.try_into()?,
|
||||
rev.to_string(),
|
||||
)))
|
||||
} else {
|
||||
Ok(NamedVersionable::PackageName(s.parse()?))
|
||||
Ok(AnyPackageIdentifier::PackageName(s.parse()?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue