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 subcommand #140

Merged
merged 27 commits into from
Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
22663f0
Refactored TempProject into its own module and started on tests
u-train Jun 9, 2023
959726f
Created test projects with a diamond-graph
u-train Jun 9, 2023
cd5f9bd
Added to primary-registry
u-train Jun 9, 2023
5932fc0
Built the update subcommand.
u-train Jun 9, 2023
7b82533
Lockfiles need to be written!
u-train Jun 9, 2023
d6d1c99
Fixed misspelling of dependency_changes
u-train Jun 10, 2023
0348913
Reverted test_registry arg back to invisible
u-train Jun 10, 2023
489fcf3
Changed target_packages to package_specs
u-train Jun 10, 2023
8f1419d
Downgrading was added as a concept
u-train Jun 10, 2023
44936fb
Added lockfile method to view packages as packageIds
u-train Jun 11, 2023
d898ebf
Installs the new packages now, forgor.
u-train Jun 11, 2023
778a954
No longer throws if missing lockfile
u-train Jun 11, 2023
f827188
Clarity on the filtering process for try_to_use
u-train Jun 11, 2023
62927d5
Added needed imports
u-train Jun 11, 2023
5659da2
Made the clippy happy and fixed grammar mistakes
u-train Jun 11, 2023
5be9f42
Delete root from registry
u-train Jun 11, 2023
bad89dd
Add new tests, almost done with them all so far.
u-train Jun 11, 2023
a32bfed
Added the rest of snapshots missing
u-train Jun 11, 2023
a702584
Testing now works, yay!
u-train Jun 12, 2023
f437589
Deleted testing code by mistake and forgot snap
u-train Jun 12, 2023
aeffc50
Appleasing the formatter
u-train Jun 12, 2023
839c713
Made it look... *pretty* and cleaned messes
u-train Jun 12, 2023
ef4f340
another blood sarciface for rust fmt
u-train Jun 12, 2023
f67687d
Doing final cleanups per comments upstream
u-train Jun 12, 2023
0812efe
The gods demand it, another formatter sacrifice
u-train Jun 12, 2023
a71211e
The coolness must be toned down.
u-train Jun 13, 2023
18fdfe0
A little silly mistake indeed
u-train Jun 13, 2023
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
4 changes: 2 additions & 2 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl Args {
Subcommand::Init(subcommand) => subcommand.run(),
Subcommand::Login(subcommand) => subcommand.run(),
Subcommand::Logout(subcommand) => subcommand.run(),
Subcommand::Update(subcommand) => subcommand.run(),
Subcommand::Update(subcommand) => subcommand.run(self.global),
Subcommand::Search(subcommand) => subcommand.run(),
Subcommand::Package(subcommand) => subcommand.run(),
Subcommand::Install(subcommand) => subcommand.run(self.global),
Expand All @@ -55,7 +55,7 @@ pub struct GlobalOptions {
pub verbosity: u8,

/// Flag to indidate if we will be using a test registry. Usable only by tests.
#[structopt(skip)]
u-train marked this conversation as resolved.
Show resolved Hide resolved
#[structopt(global = true, long = "test-registry")]
pub test_registry: bool,

/// Specify if the package index should be temporary (to prevent multiple use conflicts). Usable only by tests.
Expand Down
203 changes: 200 additions & 3 deletions src/commands/update.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,208 @@
use std::collections::BTreeSet;
use std::path::PathBuf;
use std::str::FromStr;

use crate::lockfile::{LockPackage, Lockfile};
use crate::manifest::Manifest;
use crate::package_id::PackageId;
use crate::package_name::PackageName;
use crate::package_req::PackageReq;
use crate::package_source::{PackageSource, PackageSourceMap, Registry, TestRegistry};
use crate::{resolution, GlobalOptions};
use structopt::StructOpt;

/// Update all of the dependencies of this project.
#[derive(Debug, StructOpt)]
pub struct UpdateSubcommand {}
pub struct UpdateSubcommand {
/// An optional list of dependencies to update.
/// They must be valid package name with an optional version requirement.
pub target_packages: Vec<TargetPackage>,

/// Path to the project to publish.
#[structopt(long = "project-path", default_value = ".")]
pub project_path: PathBuf,
u-train marked this conversation as resolved.
Show resolved Hide resolved
}

impl UpdateSubcommand {
pub fn run(self) -> anyhow::Result<()> {
todo!("The 'update' subcommand")
pub fn run(self, global: GlobalOptions) -> anyhow::Result<()> {
let manifest = Manifest::load(&self.project_path)?;

let lockfile = match Lockfile::load(&self.project_path)? {
Some(lockfile) => lockfile,
None => {
anyhow::bail!("Missing lockfile!")
u-train marked this conversation as resolved.
Show resolved Hide resolved
}
};

let default_registry: Box<PackageSource> = if global.test_registry {
Box::new(PackageSource::TestRegistry(TestRegistry::new(
&manifest.package.registry,
)))
} else {
Box::new(PackageSource::Registry(Registry::from_registry_spec(
&manifest.package.registry,
)?))
};

let mut package_sources = PackageSourceMap::new(default_registry);
package_sources.add_fallbacks()?;

// If the user didn't specify any targets, then update all of the packages.
// Otherwise, find the target packages to update.
let try_to_use = if self.target_packages.is_empty() {
BTreeSet::new()
} else {
lockfile
.packages
.iter()
.map(|lock_package| match lock_package {
LockPackage::Registry(lock_package) => {
PackageId::new(lock_package.name.clone(), lock_package.version.clone())
}
LockPackage::Git(_) => todo!(),
})
u-train marked this conversation as resolved.
Show resolved Hide resolved
// We update the target packages by removing the package from the list of packages to try to keep.
.filter(|package_id| {
!self.target_packages
.iter()
.any(|target_package| match target_package {
TargetPackage::Named(named_target) => package_id.name() == named_target,
TargetPackage::Required(required_target) => {
required_target.matches(package_id.name(), package_id.version())
}
})
})
.collect()
};

let resolved_graph = resolution::resolve(&manifest, &try_to_use, &package_sources)?;

// Lockfile::from_resolve(&resolved_graph).save(&self.project_path)?;
let dependency_changes = generate_depedency_changes(
&lockfile
.packages
.iter()
.map(|lock_package| match lock_package {
LockPackage::Registry(lock_package) => {
PackageId::new(lock_package.name.clone(), lock_package.version.clone())
}
LockPackage::Git(_) => todo!(),
})
.collect(),
&resolved_graph.activated,
);

render_update_difference(&dependency_changes);
Ok(())
}
}

#[derive(Debug, PartialEq, Eq, Hash)]
pub enum TargetPackage {
Named(PackageName),
Required(PackageReq),
}

impl FromStr for TargetPackage {
type Err = anyhow::Error;

fn from_str(value: &str) -> anyhow::Result<Self> {
if let Ok(package_req) = value.parse() {
Ok(TargetPackage::Required(package_req))
} else if let Ok(package_name) = value.parse() {
Ok(TargetPackage::Named(package_name))
u-train marked this conversation as resolved.
Show resolved Hide resolved
} else {
// TODO: Give better error message here, I guess.
anyhow::bail!("Was unable to parse into a package requirement or a package named.")
}
}
}

enum DependencyChange {
Added(PackageId),
Removed(PackageId),
Updated { from: PackageId, to: PackageId },
}

fn generate_depedency_changes(
old_dependencies: &BTreeSet<PackageId>,
new_dependencies: &BTreeSet<PackageId>,
) -> Vec<DependencyChange> {
let added_packages = new_dependencies.difference(&old_dependencies);
let removed_packages = old_dependencies.difference(&new_dependencies);
let changed_dependencies: BTreeSet<&PackageName> = added_packages
.clone()
.chain(removed_packages.clone())
.map(|package| package.name())
.collect();

let mut depedency_changes = Vec::new();

for dependency_name in changed_dependencies {
let matching_packages_removed = removed_packages
.clone()
.filter(|x| *x.name() == *dependency_name);
let matching_packages_added = added_packages
.clone()
.filter(|x| *x.name() == *dependency_name);

match (
matching_packages_added.clone().count(),
matching_packages_removed.clone().count(),
) {
(1, 1) => {
// We know for certain that there is only one item in the iterator.
let package_added = matching_packages_added.last().unwrap();
let package_removed = matching_packages_removed.last().unwrap();
depedency_changes.push(DependencyChange::Updated {
from: package_added.clone(),
to: package_removed.clone(),
});
}
(0, 1) => {
// We know for certain that there is only one item in the iterator.
let package_removed = matching_packages_removed.last().unwrap();
depedency_changes.push(DependencyChange::Removed(package_removed.clone()));
}
(1, 0) => {
// We know for certain that there is only one item in the iterator.
let package_added = matching_packages_added.last().unwrap();
depedency_changes.push(DependencyChange::Added(package_added.clone()));
}
(0, 0) => panic!("Impossible for the package name {} to not be removed or added if found in earlier.", dependency_name),
(_, _) => {
let mut found_changes = matching_packages_added
.map(|package| DependencyChange::Added(package.clone()))
.chain(
matching_packages_removed
.map(|package| DependencyChange::Removed(package.clone())),
)
.collect();
depedency_changes.append(&mut found_changes)
}
}
}

depedency_changes
u-train marked this conversation as resolved.
Show resolved Hide resolved
}

fn render_update_difference(dependency_changes: &Vec<DependencyChange>) {
for dependecy_change in dependency_changes.iter() {
u-train marked this conversation as resolved.
Show resolved Hide resolved
match dependecy_change {
DependencyChange::Added(package_id) => {
println!("Added {} v{}", package_id.name(), package_id.version());
}
DependencyChange::Removed(package_id) => {
println!("Removed {} v{}", package_id.name(), package_id.version());
}
DependencyChange::Updated { from, to } => {
println!(
"Updated {} from v{} to v{}",
from.name(),
from.version(),
to.version()
);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "direct-dependency-a",
"tree": {
"Packages": {
"$path": "Packages"
},
"$path": "src"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
local Indirect = require(script.Parent.Indirect)

return if Indirect == 420 then "Bingo!" else "Naw..."
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "diamond-graph/direct-dependency-a"
version = "0.1.0"
license = "MIT"
realm = "server"
registry = "test-registries/primary-registry"

[server-dependencies]
Indirect = "diamond-graph/[email protected]"
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "direct-dependency-a",
"tree": {
"Packages": {
"$path": "Packages"
},
"$path": "src"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
local Indirect = require(script.Parent.Indirect)

return if Indirect == 420 then "Bingo!" else "Naw..."
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "diamond-graph/direct-dependency-a"
version = "0.1.1"
license = "MIT"
realm = "server"
registry = "test-registries/primary-registry"

[server-dependencies]
Indirect = "diamond-graph/[email protected]"
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "direct-dependency-b",
"tree": {
"Packages": {
"$path": "Packages"
},
"$path": "src"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
local Indirect = require(script.Parent.Indirect)

return Indirect == 420
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "diamond-graph/direct-dependency-b"
version = "0.1.0"
license = "MIT"
realm = "server"
registry = "test-registries/primary-registry"

[server-dependencies]
Indirect = "diamond-graph/[email protected]"
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "indirect-dependency-a",
"tree": {
"$path": "src"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
return function()
return 420
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "diamond-graph/indirect-dependency-a"
version = "0.1.0"
license = "MIT"
realm = "server"
registry = "test-registries/primary-registry"
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "indirect-dependency-a",
"tree": {
"$path": "src"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- Zesty
return function()
return 420
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "diamond-graph/indirect-dependency-a"
version = "0.1.1"
license = "MIT"
realm = "server"
registry = "test-registries/primary-registry"
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "indirect-dependency-a",
"tree": {
"$path": "src"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
return function()
return 42
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "diamond-graph/indirect-dependency-a"
version = "0.2.0"
license = "MIT"
realm = "server"
registry = "test-registries/primary-registry"
9 changes: 9 additions & 0 deletions test-projects/diamond-graph/root/0.1.0/default.project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "root",
"tree": {
"Packages": {
"$path": "Packages"
},
"$path": "src"
}
}
4 changes: 4 additions & 0 deletions test-projects/diamond-graph/root/0.1.0/src/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
local A = require(script.Parent.A)
local B = require(script.Parent.B)

return `{A} {B}`
Loading