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

Use Package for locks and installed shards #428

Merged
merged 5 commits into from
Aug 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 spec/integration/spec_helper.cr
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def assert_installed(name, version = nil, file = __FILE__, line = __LINE__, *, g

if dependency && version
expected_version = git ? "#{version}+git.commit.#{git}" : version
dependency.requirement.should eq(version expected_version), file, line
dependency.version.should eq(version expected_version), file, line
end

if dependency && source
Expand Down Expand Up @@ -210,7 +210,7 @@ def assert_locked(name, version = nil, file = __FILE__, line = __LINE__, *, git

if lock && version
expected_version = git ? "#{version}+git.commit.#{git}" : version
actual_value = lock.requirement.as(Shards::Version).value
actual_value = lock.version.value
assert expected_version == actual_value, "expected #{name} dependency to have been locked at version #{version} instead of #{actual_value}", file, line
end

Expand Down
4 changes: 2 additions & 2 deletions spec/unit/info_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ module Shards
File.write File.join(install_path, ".shards.info"), SAMPLE_INFO
info = Info.new
info.installed.should eq({
"foo" => Dependency.new("foo", GitResolver.new("foo", "https://example.com/foo.git"), version "1.2.3"),
"foo" => Package.new("foo", GitResolver.new("foo", "https://example.com/foo.git"), version "1.2.3"),
})
end

it "save changes" do
info = Info.new
dep = Dependency.new("foo", GitResolver.new("foo", "https://example.com/foo.git"), version "1.2.3")
dep = Package.new("foo", GitResolver.new("foo", "https://example.com/foo.git"), version "1.2.3")
info.installed["foo"] = dep
info.save

Expand Down
18 changes: 10 additions & 8 deletions spec/unit/lock_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ require "../../src/lock"
module Shards
describe Lock do
it "parses" do
create_git_repository "library", "0.1.0"

lock = Lock.from_yaml <<-YAML
version: 1.0
shards:
repo:
github: user/repo
version: 1.2.3
example:
git: https://example.com/example-crystal.git
commit: 0d246ee6c52d4e758651b8669a303f04be9a2a96
git: #{git_url(:library)}
commit: #{git_commits(:library)[0]}
new_git:
git: https://example.com/new.git
version: 1.2.3+git.commit.0d246ee6c52d4e758651b8669a303f04be9a2a96
Expand All @@ -28,22 +30,22 @@ module Shards

shards[0].name.should eq("repo")
shards[0].resolver.should eq(GitResolver.new("repo", "https://github.com/user/repo.git"))
shards[0].requirement.should eq(version "1.2.3")
shards[0].version.should eq(version "1.2.3")
shards[0].to_s.should eq("repo (1.2.3)")

shards[1].name.should eq("example")
shards[1].resolver.should eq(GitResolver.new("example", "https://example.com/example-crystal.git"))
shards[1].requirement.should eq(commit "0d246ee6c52d4e758651b8669a303f04be9a2a96")
shards[1].to_s.should eq("example (commit 0d246ee)")
shards[1].resolver.should eq(GitResolver.new("example", git_url(:library)))
shards[1].version.should eq(version "0.1.0+git.commit.#{git_commits(:library)[0]}")
shards[1].to_s.should eq("example (0.1.0 at #{git_commits(:library)[0][0...7]})")

shards[2].name.should eq("new_git")
shards[2].resolver.should eq(GitResolver.new("new_git", "https://example.com/new.git"))
shards[2].requirement.should eq(version "1.2.3+git.commit.0d246ee6c52d4e758651b8669a303f04be9a2a96")
shards[2].version.should eq(version "1.2.3+git.commit.0d246ee6c52d4e758651b8669a303f04be9a2a96")
shards[2].to_s.should eq("new_git (1.2.3 at 0d246ee)")

shards[3].name.should eq("new_path")
shards[3].resolver.should eq(PathResolver.new("new_path", "../path"))
shards[3].requirement.should eq(version "0.1.2")
shards[3].version.should eq(version "0.1.2")
shards[3].to_s.should eq("new_path (0.1.2 at ../path)")
end

Expand Down
17 changes: 6 additions & 11 deletions src/commands/check.cr
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,13 @@ module Shards
return false
end

if version = lock.requirement.as?(Shards::Version)
if !dependency.matches?(version)
Log.debug { "#{dependency.name}: lock conflict" }
return false
else
package = Package.new(lock.name, lock.resolver, version)
return false unless package.installed?
verify(package.spec.dependencies)
return true
end
if !dependency.matches?(lock.version)
Log.debug { "#{dependency.name}: lock conflict" }
return false
else
raise Error.new("Invalid #{LOCK_FILENAME}. Please run `shards install` to fix it.")
return false unless lock.installed?
verify(lock.spec.dependencies)
return true
end
end
end
Expand Down
9 changes: 3 additions & 6 deletions src/commands/install.cr
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,8 @@ module Shards
if lock = locks.shards.find { |d| d.name == package.name }
if lock.resolver != package.resolver
raise LockConflict.new("#{package.name} source changed")
elsif version = lock.requirement.as?(Shards::Version)
validate_locked_version(package, version)
else
raise InvalidLock.new # unknown lock resolver
validate_locked_version(package, lock.version)
end
else
raise LockConflict.new("can't install new dependency #{package.name} in production")
Expand Down Expand Up @@ -90,9 +88,8 @@ module Shards
private def outdated_lockfile?(packages)
return true if locks.version != Shards::Lock::CURRENT_VERSION
return true if packages.size != locks.shards.size
a = packages.to_h { |x| {x.name, {x.resolver.class.key, x.resolver.source, x.version}} }
b = locks.shards.to_h { |x| {x.name, {x.resolver.class.key, x.resolver.source, x.requirement.as?(Shards::Version)}} }
a != b

packages.index_by(&.name) != locks.shards.index_by(&.name)
end
end
end
Expand Down
10 changes: 3 additions & 7 deletions src/commands/list.cr
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,14 @@ module Shards

private def list(dependencies, level = 1)
dependencies.each do |dependency|
installed = Shards.info.installed[dependency.name]?
unless installed
package = Shards.info.installed[dependency.name]?
unless package
Log.debug { "#{dependency.name}: not installed" }
raise Error.new("Dependencies aren't satisfied. Install them with 'shards install'")
end

version = installed.requirement.as(Shards::Version)
package = Package.new(installed.name, installed.resolver, version)
resolver = installed.resolver

indent = " " * level
puts "#{indent}* #{dependency.name} (#{resolver.report_version version})"
puts "#{indent}* #{package}"

indent_level = @tree ? level + 1 : level
list(package.spec.dependencies, indent_level)
Expand Down
2 changes: 1 addition & 1 deletion src/commands/outdated.cr
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ module Shards
end

resolver = package.resolver
installed = installed_dep.requirement.as(Shards::Version)
installed = installed_dep.version

# already the latest version?
available_versions =
Expand Down
23 changes: 18 additions & 5 deletions src/dependency.cr
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module Shards
def initialize(@name : String, @resolver : Resolver, @requirement : Requirement = Any)
end

def self.from_yaml(pull : YAML::PullParser, *, is_lock = false)
def self.from_yaml(pull : YAML::PullParser)
mapping_start = pull.location
name = pull.read_scalar
pull.read_mapping do
Expand Down Expand Up @@ -40,10 +40,6 @@ module Shards
resolver = resolver_data[:type].find_resolver(resolver_data[:key], name, resolver_data[:source])

requirement = resolver.parse_requirement(params)
if is_lock && requirement.is_a?(VersionReq)
requirement = Version.new(requirement.to_s)
end

Dependency.new(name, resolver, requirement)
end
end
Expand All @@ -57,6 +53,23 @@ module Shards
end
end

def as_package?
version =
case req = @requirement
when VersionReq then Version.new(req.to_s)
else
# This conversion is used to keep compatibility
# with old versions (1.0) of lock files.
versions = @resolver.versions_for(req)
unless versions.size == 1
return
end
versions.first
end

Package.new(@name, @resolver, version)
end

def_equals @name, @resolver, @requirement

def prerelease?
Expand Down
2 changes: 1 addition & 1 deletion src/info.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ require "./lock"

class Shards::Info
getter install_path : String
getter installed = Hash(String, Dependency).new
getter installed = Hash(String, Package).new

def initialize(@install_path = Shards.install_path)
reload
Expand Down
20 changes: 15 additions & 5 deletions src/lock.cr
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
require "./ext/yaml"
require "./dependency"
require "./package"

module Shards
class Lock
property version : String
property shards : Array(Dependency)
property shards : Array(Package)

CURRENT_VERSION = "2.0"

def initialize(@version : String, @shards : Array(Dependency))
def initialize(@version : String, @shards : Array(Package))
end

def self.from_file(path)
Expand All @@ -17,7 +18,7 @@ module Shards
end

def self.from_yaml(str)
dependencies = [] of Dependency
shards = [] of Package

pull = YAML::PullParser.new(str)
pull.read_stream do
Expand All @@ -32,13 +33,22 @@ module Shards
case key = pull.read_scalar
when "shards"
pull.each_in_mapping do
dependencies << Dependency.from_yaml(pull, is_lock: true)
# Shards are parsed as dependencies to keep
# compatiblity with version 1.0. Calls to `as_package?`
# will use the resolver to convert potential references
# to explicit versions used in 2.0 format.
dep = Dependency.from_yaml(pull)
bcardiff marked this conversation as resolved.
Show resolved Hide resolved
if package = dep.as_package?
shards << package
else
Log.warn { "Lock for shard \"#{dep.name}\" is invalid" }
end
end
else
pull.raise "No such attribute #{key} in lock version #{version}"
end

Lock.new(version, dependencies)
Lock.new(version, shards)
end
end
end
Expand Down
28 changes: 6 additions & 22 deletions src/molinillo_solver.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ require "./package"

module Shards
class MolinilloSolver
setter locks : Array(Dependency)?
setter locks : Array(Package)?
@solution : Array(Package)?
@prereleases : Bool
@ignore_crystal_version : Bool
Expand All @@ -21,27 +21,15 @@ module Shards

private def add_lock(base, lock_index, dep : Dependency)
if lock = lock_index.delete(dep.name)
lock_version =
case lock_req = lock.requirement
when Version then lock_req
else
versions = lock.resolver.versions_for(lock_req)
unless versions.size == 1
Log.warn { "Lock for shard \"#{dep.name}\" is invalid" }
return
end
lock.requirement = versions.first
end

check_single_resolver_by_name dep.resolver
base.add_vertex(lock.name, lock, true)
base.add_vertex(lock.name, Dependency.new(lock.name, dep.resolver, lock.version), true)

# Use the resolver from dependencies (not lock) if available.
# This is to allow changing source without bumping the version when possible.
if dep.resolver != lock.resolver
Log.warn { "Ignoring source of \"#{dep.name}\" on shard.lock" }
end
spec = dep.resolver.spec(lock_version)
spec = dep.resolver.spec(lock.version)

add_lock base, lock_index, apply_overrides(spec.dependencies)
end
Expand All @@ -50,10 +38,7 @@ module Shards
private def add_lock(base, lock_index, deps : Array(Dependency))
deps.each do |dep|
if lock = lock_index[dep.name]?
if version = lock.requirement.as?(Version)
next unless dep.matches?(version)
end

next unless dep.matches?(lock.version)
add_lock(base, lock_index, dep)
end
end
Expand Down Expand Up @@ -215,9 +200,8 @@ module Shards

if (locks = @locks) &&
(locked = locks.find { |dep| dep.name == dependency.name }) &&
(locked_version = locked.requirement.as?(Version)) &&
dependency.matches?(locked_version)
matching << locked_version
dependency.matches?(locked.version)
matching << locked.version
end

matching.uniq
Expand Down
14 changes: 12 additions & 2 deletions src/package.cr
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ module Shards
def initialize(@name, @resolver, @version, @is_override = false)
end

def_equals @name, @resolver, @version
bcardiff marked this conversation as resolved.
Show resolved Hide resolved

def report_version
resolver.report_version(version)
end
Expand Down Expand Up @@ -44,7 +46,7 @@ module Shards
def installed?
return false unless File.exists?(install_path)
if installed = Shards.info.installed[name]?
installed.resolver == resolver && installed.requirement == version
installed.resolver == resolver && installed.version == version
else
false
end
Expand All @@ -70,7 +72,7 @@ module Shards
File.symlink(target, lib_path)
end

Shards.info.installed[name] = Dependency.new(name, resolver, version)
Shards.info.installed[name] = self
Shards.info.save
end

Expand Down Expand Up @@ -115,5 +117,13 @@ module Shards
end
end
end

def to_yaml(builder)
Dependency.new(name, resolver, version).to_yaml(builder)
end

def to_s(io)
io << name << " (" << report_version << ")"
end
end
end
1 change: 1 addition & 0 deletions src/resolvers/git.cr
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require "uri"
require "./resolver"
require "../versions"
require "../logger"
require "../helpers/path"

module Shards
Expand Down