Merge branch 'master' into vrc-get-tauri

This commit is contained in:
anatawa12 2024-02-26 22:01:08 +09:00 committed by GitHub
commit 730a5f95fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 422 additions and 56 deletions

View file

@ -8,6 +8,7 @@ The format is based on [Keep a Changelog].
## [Unreleased]
### Added
- `vrc-get downgrade` which is for downgrading package `#409`
### Changed

View file

@ -27,6 +27,7 @@ use std::path::{Path, PathBuf};
use crate::io::{DirEntry, FileSystemProjectIo, ProjectIo};
use crate::PackageJson;
pub use add_package::AddPackageErr;
pub use add_package::AddPackageOperation;
pub use call_unity::ExecuteUnityError;
pub use migrate_unity_2022::MigrateUnity2022Error;
pub use pending_project_changes::PendingProjectChanges;

View file

@ -1,8 +1,10 @@
use crate::io::ProjectIo;
use crate::unity_project::pending_project_changes::RemoveReason;
use crate::unity_project::vpm_manifest::VpmManifest;
use crate::unity_project::{package_resolution, PendingProjectChanges};
use crate::version::DependencyRange;
use crate::{PackageCollection, PackageInfo, UnityProject};
use log::debug;
use std::fmt;
#[derive(Debug)]
@ -10,6 +12,8 @@ use std::fmt;
pub enum AddPackageErr {
DependencyNotFound { dependency_name: Box<str> },
UpgradingNonLockedPackage { package_name: Box<str> },
DowngradingNonLockedPackage { package_name: Box<str> },
UpgradingWithDowngrade { package_name: Box<str> },
}
impl fmt::Display for AddPackageErr {
@ -23,12 +27,28 @@ impl fmt::Display for AddPackageErr {
f,
"Package {package_name} is not locked, so it cannot be upgraded"
),
AddPackageErr::DowngradingNonLockedPackage { package_name } => write!(
f,
"Package {package_name} is not locked, so it cannot be downgraded"
),
AddPackageErr::UpgradingWithDowngrade { package_name } => write!(
f,
"Package {package_name} is locked, so it cannot be downgraded"
),
}
}
}
impl std::error::Error for AddPackageErr {}
#[non_exhaustive]
#[derive(Debug)]
pub enum AddPackageOperation {
InstallToDependencies,
UpgradeLocked,
Downgrade,
}
// adding package
impl<IO: ProjectIo> UnityProject<IO> {
/// Creates a new `AddPackageRequest` to add the specified packages.
@ -38,7 +58,7 @@ impl<IO: ProjectIo> UnityProject<IO> {
&self,
env: &'env impl PackageCollection,
packages: Vec<PackageInfo<'env>>,
to_dependencies: bool,
operation: AddPackageOperation,
allow_prerelease: bool,
) -> Result<PendingProjectChanges<'env>, AddPackageErr> {
// if same or newer requested package is in locked dependencies,
@ -48,36 +68,104 @@ impl<IO: ProjectIo> UnityProject<IO> {
let mut changes = super::pending_project_changes::Builder::new();
for request in packages {
if to_dependencies {
let add_to_dependencies = self
.manifest
.get_dependency(request.name())
.and_then(|range| range.as_single_version())
.map(|full| &full < request.version())
.unwrap_or(true);
match operation {
AddPackageOperation::InstallToDependencies => {
let add_to_dependencies = self
.manifest
.get_dependency(request.name())
.and_then(|range| range.as_single_version())
.map(|full| &full < request.version())
.unwrap_or(true);
if add_to_dependencies {
changes.add_to_dependencies(
request.name().into(),
DependencyRange::version(request.version().clone()),
if add_to_dependencies {
debug!("Adding package {} to dependencies", request.name());
changes.add_to_dependencies(
request.name().into(),
DependencyRange::version(request.version().clone()),
);
}
check_and_add_adding_package(request, &mut adding_packages, &self.manifest);
}
AddPackageOperation::UpgradeLocked => {
if self.manifest.get_locked(request.name()).is_none() {
// if package is not locked, it cannot be updated
return Err(AddPackageErr::UpgradingNonLockedPackage {
package_name: request.name().into(),
});
}
check_and_add_adding_package(request, &mut adding_packages, &self.manifest);
}
AddPackageOperation::Downgrade => {
let Some(locked_version) = self.manifest.get_locked(request.name()) else {
// if package is not locked, it cannot be updated
return Err(AddPackageErr::DowngradingNonLockedPackage {
package_name: request.name().into(),
});
};
if locked_version.version() < request.version() {
// if the locked version is older than the requested version,
// it cannot be downgraded
return Err(AddPackageErr::UpgradingWithDowngrade {
package_name: request.name().into(),
});
}
let downgrade_dependencies = self
.manifest
.get_dependency(request.name())
.map(|range| !range.matches(request.version()))
.unwrap_or(false);
if downgrade_dependencies {
debug!(
"Downgrading package {} to version {} in dependencies",
request.name(),
request.version()
);
// downgrade to >= requested version
changes.add_to_dependencies(
request.name().into(),
DependencyRange::version(request.version().clone()),
);
}
// always add to adding_packages since downloading
debug!(
"Adding package {} to locked packages at version {}",
request.name(),
request.version()
);
adding_packages.push(request);
}
}
if !to_dependencies && self.manifest.get_locked(request.name()).is_none() {
// if package is not locked, it cannot be updated
return Err(AddPackageErr::UpgradingNonLockedPackage {
package_name: request.name().into(),
});
}
if self
.manifest
.get_locked(request.name())
.map(|version| version.version() < request.version())
.unwrap_or(true)
{
adding_packages.push(request);
fn check_and_add_adding_package<'env>(
request: PackageInfo<'env>,
adding_packages: &mut Vec<PackageInfo<'env>>,
manifest: &VpmManifest,
) {
if manifest
.get_locked(request.name())
.map(|version| version.version() < request.version())
.unwrap_or(true)
{
debug!(
"Adding package {} to locked packages at version {}",
request.name(),
request.version()
);
adding_packages.push(request);
} else {
debug!(
"Package {} is already locked at newer version than {}: version {}",
request.name(),
request.version(),
manifest.get_locked(request.name()).unwrap().version()
);
}
}
}
@ -87,7 +175,13 @@ impl<IO: ProjectIo> UnityProject<IO> {
}
let result = package_resolution::collect_adding_packages(
self.manifest.dependencies(),
self.manifest.dependencies().map(|(name, original_range)| {
if let Some(new_range) = changes.get_dependencies(name) {
(name, new_range)
} else {
(name, original_range)
}
}),
self.manifest.all_locked(),
|pkg| self.manifest.get_locked(pkg),
self.unity_version(),

View file

@ -1,7 +1,7 @@
use crate::io;
use crate::io::ProjectIo;
use crate::traits::EnvironmentIoHolder;
use crate::unity_project::AddPackageErr;
use crate::unity_project::{AddPackageErr, AddPackageOperation};
use crate::version::UnityVersion;
use crate::{PackageCollection, RemotePackageDownloader, UnityProject, VersionSelector};
use log::warn;
@ -107,7 +107,12 @@ where
if !packages.is_empty() {
// install packages
let request = project
.add_package_request(env, packages, true, false)
.add_package_request(
env,
packages,
AddPackageOperation::InstallToDependencies,
false,
)
.await?;
project.apply_pending_changes(env, request).await?;
}

View file

@ -272,6 +272,13 @@ impl<'env> Builder<'env> {
self
}
pub(crate) fn get_dependencies(&self, name: &str) -> Option<&DependencyRange> {
self.package_changes
.get(name)
.and_then(|x| x.as_install())
.and_then(|x| x.to_dependencies.as_ref())
}
pub(crate) fn get_installing(&self, name: &str) -> Option<PackageInfo<'env>> {
self.package_changes
.get(name)

View file

@ -34,8 +34,10 @@ impl From<AddPackageErr> for ResolvePackageErr {
AddPackageErr::DependencyNotFound { dependency_name } => {
Self::DependencyNotFound { dependency_name }
}
AddPackageErr::UpgradingNonLockedPackage { .. } => {
panic!("AddPackageErr::UpgradingNonLockedPackage should not be happened")
AddPackageErr::UpgradingNonLockedPackage { .. }
| AddPackageErr::DowngradingNonLockedPackage { .. }
| AddPackageErr::UpgradingWithDowngrade { .. } => {
panic!("{value:?} should not be happened")
}
}
}

View file

@ -3,7 +3,7 @@ use futures::executor::block_on;
use std::collections::HashSet;
use std::path::Path;
use vrc_get_vpm::unity_project::pending_project_changes::RemoveReason;
use vrc_get_vpm::unity_project::AddPackageErr;
use vrc_get_vpm::unity_project::{AddPackageErr, AddPackageOperation};
use vrc_get_vpm::version::Version;
use vrc_get_vpm::PackageJson;
@ -28,7 +28,12 @@ fn add_to_dependencies() {
let base_package = collection.get_package("com.vrchat.base", Version::new(1, 0, 0));
let result = project
.add_package_request(&collection, vec![avatars_package], true, false)
.add_package_request(
&collection,
vec![avatars_package],
AddPackageOperation::InstallToDependencies,
false,
)
.await
.unwrap();
@ -68,7 +73,12 @@ fn install_already_installed_in_locked_to_locked() {
let avatars_package = collection.get_package("com.vrchat.avatars", Version::new(1, 0, 0));
let result = project
.add_package_request(&collection, vec![avatars_package], false, false)
.add_package_request(
&collection,
vec![avatars_package],
AddPackageOperation::UpgradeLocked,
false,
)
.await
.unwrap();
@ -105,7 +115,12 @@ fn install_already_installed_in_locked_to_dependencies() {
let base_package = collection.get_package("com.vrchat.base", Version::new(1, 0, 0));
let result = project
.add_package_request(&collection, vec![base_package], true, false)
.add_package_request(
&collection,
vec![base_package],
AddPackageOperation::InstallToDependencies,
false,
)
.await
.unwrap();
@ -144,7 +159,12 @@ fn install_already_installed_in_dependencies_to_dependencies() {
let avatars_package = collection.get_package("com.vrchat.avatars", Version::new(1, 0, 0));
let result = project
.add_package_request(&collection, vec![avatars_package], true, false)
.add_package_request(
&collection,
vec![avatars_package],
AddPackageOperation::InstallToDependencies,
false,
)
.await
.unwrap();
@ -181,7 +201,12 @@ fn upgrading_unused_packages() {
let base_package = collection.get_package("com.vrchat.base", Version::new(1, 1, 0));
let result = project
.add_package_request(&collection, vec![avatars_package], false, false)
.add_package_request(
&collection,
vec![avatars_package],
AddPackageOperation::UpgradeLocked,
false,
)
.await
.unwrap();
@ -224,7 +249,12 @@ fn transitive_unused_remove_with_upgrade() {
let package = collection.get_package("com.anatawa12.package", Version::new(1, 1, 0));
let result = project
.add_package_request(&collection, vec![package], false, false)
.add_package_request(
&collection,
vec![package],
AddPackageOperation::UpgradeLocked,
false,
)
.await
.unwrap();
@ -268,7 +298,12 @@ fn do_not_remove_transitively_when_untouched() {
let package = collection.get_package("com.anatawa12.package", Version::new(1, 1, 0));
let result = project
.add_package_request(&collection, vec![package], false, false)
.add_package_request(
&collection,
vec![package],
AddPackageOperation::UpgradeLocked,
false,
)
.await
.unwrap();
@ -302,7 +337,12 @@ fn remove_legacy_package_when_install() {
let package = collection.get_package("com.anatawa12.package", Version::new(1, 1, 0));
let result = project
.add_package_request(&collection, vec![package], true, false)
.add_package_request(
&collection,
vec![package],
AddPackageOperation::InstallToDependencies,
false,
)
.await
.unwrap();
@ -341,7 +381,12 @@ fn remove_legacy_package_when_upgrade() {
let package = collection.get_package("com.anatawa12.package", Version::new(1, 1, 0));
let result = project
.add_package_request(&collection, vec![package], false, false)
.add_package_request(
&collection,
vec![package],
AddPackageOperation::UpgradeLocked,
false,
)
.await
.unwrap();
@ -385,7 +430,12 @@ fn remove_referenced_legacy_package_when_install() {
let package = collection.get_package("com.anatawa12.package", Version::new(1, 1, 0));
let result = project
.add_package_request(&collection, vec![package], true, false)
.add_package_request(
&collection,
vec![package],
AddPackageOperation::InstallToDependencies,
false,
)
.await
.unwrap();
@ -431,7 +481,12 @@ fn legacy_assets_by_path() {
let package = collection.get_package("com.anatawa12.package", Version::new(1, 0, 0));
let result = project
.add_package_request(&collection, vec![package], true, false)
.add_package_request(
&collection,
vec![package],
AddPackageOperation::InstallToDependencies,
false,
)
.await
.unwrap();
@ -490,7 +545,12 @@ fn legacy_assets_by_guid() {
let package = collection.get_package("com.anatawa12.package", Version::new(1, 0, 0));
let result = project
.add_package_request(&collection, vec![package], true, false)
.add_package_request(
&collection,
vec![package],
AddPackageOperation::InstallToDependencies,
false,
)
.await
.unwrap();
@ -540,7 +600,12 @@ fn deny_remove_files_not_in_assets_or_packages() {
let package = collection.get_package("com.anatawa12.package", Version::new(1, 0, 0));
let result = project
.add_package_request(&collection, vec![package], true, false)
.add_package_request(
&collection,
vec![package],
AddPackageOperation::InstallToDependencies,
false,
)
.await
.unwrap();
@ -569,7 +634,12 @@ fn deny_remove_parent_folders() {
let package = collection.get_package("com.anatawa12.package", Version::new(1, 0, 0));
let result = project
.add_package_request(&collection, vec![package], true, false)
.add_package_request(
&collection,
vec![package],
AddPackageOperation::InstallToDependencies,
false,
)
.await
.unwrap();
@ -596,7 +666,12 @@ fn deny_absolute_legacy_assets() {
let package = collection.get_package("com.anatawa12.package", Version::new(1, 0, 0));
let result = project
.add_package_request(&collection, vec![package], true, false)
.add_package_request(
&collection,
vec![package],
AddPackageOperation::InstallToDependencies,
false,
)
.await
.unwrap();
@ -627,7 +702,12 @@ fn not_found_err() {
let avatars_package = collection.get_package("com.vrchat.avatars", Version::new(1, 0, 0));
let err = project
.add_package_request(&collection, vec![avatars_package], true, false)
.add_package_request(
&collection,
vec![avatars_package],
AddPackageOperation::InstallToDependencies,
false,
)
.await
.expect_err("should fail");
@ -655,7 +735,12 @@ fn updating_non_locked_package_should_cause_error() {
let avatars_package = collection.get_package("com.vrchat.avatars", Version::new(1, 0, 0));
let err = project
.add_package_request(&collection, vec![avatars_package], false, false)
.add_package_request(
&collection,
vec![avatars_package],
AddPackageOperation::UpgradeLocked,
false,
)
.await
.expect_err("should fail");
@ -668,6 +753,76 @@ fn updating_non_locked_package_should_cause_error() {
})
}
#[test]
fn downgrade_basic() {
block_on(async {
let project = VirtualProjectBuilder::new()
.add_locked("com.vrchat.base", Version::new(1, 1, 0), &[])
.add_dependency("com.vrchat.base", Version::new(1, 0, 0))
.build()
.await
.unwrap();
let collection = PackageCollectionBuilder::new()
.add(PackageJson::new("com.vrchat.base", Version::new(1, 0, 0)))
.build();
let base_package = collection.get_package("com.vrchat.base", Version::new(1, 0, 0));
let result = project
.add_package_request(
&collection,
vec![base_package],
AddPackageOperation::Downgrade,
false,
)
.await
.unwrap();
assert_eq!(result.package_changes().len(), 1);
assert_eq!(result.remove_legacy_folders().len(), 0);
assert_eq!(result.remove_legacy_files().len(), 0);
assert_eq!(result.conflicts().len(), 0);
assert_installing_to_locked_only(&result, &base_package);
})
}
#[test]
fn downgrade_dependencies() {
block_on(async {
let project = VirtualProjectBuilder::new()
.add_locked("com.vrchat.base", Version::new(1, 1, 0), &[])
.add_dependency("com.vrchat.base", Version::new(1, 1, 0))
.build()
.await
.unwrap();
let collection = PackageCollectionBuilder::new()
.add(PackageJson::new("com.vrchat.base", Version::new(1, 0, 0)))
.build();
let base_package = collection.get_package("com.vrchat.base", Version::new(1, 0, 0));
let result = project
.add_package_request(
&collection,
vec![base_package],
AddPackageOperation::Downgrade,
false,
)
.await
.unwrap();
assert_eq!(result.package_changes().len(), 1);
assert_eq!(result.remove_legacy_folders().len(), 0);
assert_eq!(result.remove_legacy_files().len(), 0);
assert_eq!(result.conflicts().len(), 0);
assert_installing_to_both(&result, &base_package);
})
}
// endregion
// region conflicts
@ -704,7 +859,12 @@ fn conflict_requirements_of_installed_and_installing() {
let base_1_1_0 = collection.get_package("com.vrchat.base", Version::new(1, 1, 0));
let resolve = project
.add_package_request(&collection, vec![tool], true, false)
.add_package_request(
&collection,
vec![tool],
AddPackageOperation::InstallToDependencies,
false,
)
.await
.unwrap();
@ -755,7 +915,12 @@ fn conflict_already_conflicted_and_no_new_conflict() {
let tool = collection.get_package("com.anatawa12.tool", Version::new(1, 0, 0));
let resolve = project
.add_package_request(&collection, vec![tool], true, false)
.add_package_request(
&collection,
vec![tool],
AddPackageOperation::InstallToDependencies,
false,
)
.await
.unwrap();
@ -798,7 +963,12 @@ fn conflict_requirements_of_installed_and_installing_related_to_dependencies() {
let tool = collection.get_package("com.anatawa12.tool", Version::new(1, 0, 0));
let resolve = project
.add_package_request(&collection, vec![tool], true, false)
.add_package_request(
&collection,
vec![tool],
AddPackageOperation::InstallToDependencies,
false,
)
.await
.unwrap();

View file

@ -18,7 +18,7 @@ use std::str::FromStr;
use vrc_get_vpm::io::{DefaultEnvironmentIo, DefaultProjectIo};
use vrc_get_vpm::repository::RemoteRepository;
use vrc_get_vpm::unity_project::pending_project_changes::{PackageChange, RemoveReason};
use vrc_get_vpm::unity_project::PendingProjectChanges;
use vrc_get_vpm::unity_project::{AddPackageOperation, PendingProjectChanges};
use vrc_get_vpm::version::Version;
use vrc_get_vpm::{PackageCollection, PackageInfo, PackageJson, UserRepoSetting, VersionSelector};
@ -336,6 +336,7 @@ pub enum Command {
Update(Update),
Outdated(Outdated),
Upgrade(Upgrade),
Downgrade(Downgrade),
Search(Search),
#[command(subcommand)]
Repo(Repo),
@ -357,6 +358,7 @@ multi_command!(Command is
Update,
Outdated,
Upgrade,
Downgrade,
Search,
Repo,
Info,
@ -446,7 +448,12 @@ impl Install {
};
let changes = unity
.add_package_request(&env, packages, true, self.prerelease)
.add_package_request(
&env,
packages,
AddPackageOperation::InstallToDependencies,
self.prerelease,
)
.await
.exit_context("collecting packages to be installed");
@ -692,7 +699,12 @@ impl Upgrade {
};
let changes = unity
.add_package_request(&env, updates, false, self.prerelease)
.add_package_request(
&env,
updates,
AddPackageOperation::UpgradeLocked,
self.prerelease,
)
.await
.exit_context("collecting packages to be upgraded");
@ -727,6 +739,80 @@ impl Upgrade {
}
}
/// Downgrade the specified package specified version.
///
/// With install command, you'll add to dependencies. With upgrade command,
/// you'll upgrade dependencies or locked dependencies but not add to dependencies.
#[derive(Parser)]
#[command(author, version)]
pub struct Downgrade {
/// Name of Package
#[arg()]
name: String,
/// Version of package.
#[arg(id = "VERSION")]
version: Version,
/// Include prerelease
#[arg(long = "prerelease")]
prerelease: bool,
/// Path to project dir. by default CWD or parents of CWD will be used
#[arg(short = 'p', long = "project")]
project: Option<Box<Path>>,
#[command(flatten)]
env_args: EnvArgs,
/// skip confirm
#[arg(short, long)]
yes: bool,
}
impl Downgrade {
pub async fn run(self) {
let env = load_env(&self.env_args).await;
let mut unity = load_unity(self.project).await;
let updates = vec![get_package(
&env,
&self.name,
VersionSelector::specific_version(&self.version),
)];
let changes = unity
.add_package_request(
&env,
updates,
AddPackageOperation::Downgrade,
self.prerelease,
)
.await
.exit_context("collecting packages to be upgraded");
print_prompt_install(&changes);
if require_prompt_for_install(&changes, self.name.as_str(), None) {
prompt_install(self.yes)
}
let downgrades = (changes.package_changes().iter())
.filter_map(|(_, x)| x.as_install())
.filter_map(|x| x.install_package())
.map(|x| (x.name().to_owned(), x.version().clone()))
.collect::<Vec<_>>();
unity
.apply_pending_changes(&env, changes)
.await
.exit_context("upgrading packages");
for (name, version) in downgrades {
println!("downgraded {} to {}", name, version);
}
save_unity(&mut unity).await;
}
}
/// Search package by the query
///
/// Search for packages that includes query in either name, displayName, or description.