Skip to content

Commit 79b5802

Browse files
committed
Add outdated command to know which dependencies are outdated
1 parent 48b4739 commit 79b5802

File tree

5 files changed

+157
-7
lines changed

5 files changed

+157
-7
lines changed

src/cli.cr

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module Shards
1212
init - Initializes a shard folder.
1313
install - Installs dependencies from `shard.lock` file.
1414
list [--tree] - Lists installed dependencies.
15+
outdated [--pre] - Lists dependencies that are outdated.
1516
prune - Removes unused dependencies from `lib` folder.
1617
update - Updates dependencies and `shards.lock`.
1718
version [<path>] - Prints the current version of the shard.
@@ -46,6 +47,8 @@ module Shards
4647
Commands::Install.run(path)
4748
when "list"
4849
Commands::List.run(path, tree: args.includes?("--tree"))
50+
when "outdated"
51+
Commands::Outdated.run(path, prereleases: args.includes?("--pre"))
4952
when "prune"
5053
Commands::Prune.run(path)
5154
when "update"

src/commands/outdated.cr

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
require "./command"
2+
3+
module Shards
4+
module Commands
5+
class Outdated < Command
6+
@up_to_date = true
7+
@output = IO::Memory.new
8+
9+
def self.run(path, @@prereleases = false)
10+
super
11+
end
12+
13+
def run(*args)
14+
return unless has_dependencies?
15+
16+
if lockfile?
17+
manager.locks = locks
18+
manager.resolve
19+
else
20+
manager.resolve
21+
end
22+
23+
manager.packages.each do |package|
24+
analyze(package)
25+
end
26+
27+
if @up_to_date
28+
Shards.logger.info "Dependencies are up to date!"
29+
else
30+
@output.rewind
31+
Shards.logger.warn "Outdated dependencies:"
32+
puts @output.to_s
33+
end
34+
end
35+
36+
private def analyze(package)
37+
_spec = package.resolver.installed_spec
38+
39+
unless _spec
40+
Shards.logger.warn { "#{package.name}: not installed" }
41+
return
42+
end
43+
44+
installed = _spec.version
45+
46+
# already the latest version?
47+
latest = Versions.sort(package.available_versions(@@prereleases)).first
48+
return if latest == installed
49+
50+
@up_to_date = false
51+
52+
@output << " * " << package.name
53+
@output << " (installed: " << installed
54+
55+
# is new version matching constraints available?
56+
available = package.matching_versions(@@prereleases).first
57+
unless available == installed
58+
@output << ", available: " << available
59+
end
60+
61+
# also report latest version:
62+
if Versions.compare(latest, available) < 0
63+
@output << ", latest: " << latest
64+
end
65+
66+
@output.puts ')'
67+
end
68+
69+
# FIXME: duplicates Check#has_dependencies?
70+
private def has_dependencies?
71+
spec.dependencies.any? || (!Shards.production? && spec.development_dependencies.any?)
72+
end
73+
end
74+
end
75+
end

src/package.cr

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ module Shards
4040
end
4141
end
4242

43-
def matching_versions
44-
Versions.resolve(available_versions, requirements)
43+
def matching_versions(prereleases = false)
44+
Versions.resolve(available_versions, requirements, prereleases)
4545
end
4646

4747
def spec
@@ -123,8 +123,13 @@ module Shards
123123
@resolver ||= Shards.find_resolver(@dependency)
124124
end
125125

126-
private def available_versions
127-
@available_versions ||= resolver.available_versions
126+
def available_versions(prereleases = true)
127+
versions = @available_versions ||= resolver.available_versions
128+
if prereleases
129+
versions
130+
else
131+
Versions.without_prereleases(versions)
132+
end
128133
end
129134
end
130135

src/versions.cr

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,13 @@ module Shards
153153
str.each_char.any?(&.ascii_letter?)
154154
end
155155

156-
def self.resolve(versions, requirements : Enumerable(String))
157-
unless requirements.any? { |r| prerelease?(r) }
158-
versions = versions.reject { |v| prerelease?(v) }
156+
protected def self.without_prereleases(versions)
157+
versions.reject { |v| prerelease?(v) }
158+
end
159+
160+
def self.resolve(versions, requirements : Enumerable(String), prereleases = false)
161+
unless prereleases || requirements.any? { |r| prerelease?(r) }
162+
versions = without_prereleases(versions)
159163
end
160164

161165
matching_versions = requirements

test/integration/outdated_test.cr

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
require "../integration_helper"
2+
3+
class OutdatedCommandTest < Minitest::Test
4+
def test_up_to_date
5+
with_shard({dependencies: {web: "*"}}) do
6+
run "shards install"
7+
8+
stdout = run "shards outdated --no-color", capture: true
9+
assert_match "I: Dependencies are up to date!", stdout
10+
end
11+
end
12+
13+
def test_not_latest_version
14+
with_shard({dependencies: {orm: "*"}}, {orm: "0.3.1"}) do
15+
run "shards install"
16+
17+
stdout = run "shards outdated --no-color", capture: true
18+
assert_match "W: Outdated dependencies:", stdout
19+
assert_match " * orm (installed: 0.3.1, available: 0.5.0)", stdout
20+
end
21+
end
22+
23+
def test_available_version_matching_pessimistic_operator
24+
with_shard({dependencies: {orm: "~> 0.3.0"}}, {orm: "0.3.1"}) do
25+
run "shards install"
26+
27+
stdout = run "shards outdated --no-color", capture: true
28+
assert_match "W: Outdated dependencies:", stdout
29+
assert_match " * orm (installed: 0.3.1, available: 0.3.2, latest: 0.5.0)", stdout
30+
end
31+
end
32+
33+
def test_reports_new_prerelease
34+
with_shard({dependencies: {unstable: "0.3.0.alpha"}}) do
35+
run "shards install"
36+
end
37+
with_shard({dependencies: {unstable: "~> 0.3.0.alpha"}}) do
38+
stdout = run "shards outdated --no-color", capture: true
39+
assert_match "W: Outdated dependencies:", stdout
40+
assert_match " * unstable (installed: 0.3.0.alpha, available: 0.3.0.beta)", stdout
41+
end
42+
end
43+
44+
def test_wont_report_prereleases_by_default
45+
with_shard({dependencies: {preview: "*"}}, {preview: "0.2.0"}) do
46+
run "shards install"
47+
48+
stdout = run "shards outdated --no-color", capture: true
49+
assert_match "W: Outdated dependencies:", stdout
50+
assert_match " * preview (installed: 0.2.0, available: 0.3.0)", stdout
51+
end
52+
end
53+
54+
def test_reports_prereleases_when_asked
55+
with_shard({dependencies: {preview: "*"}}, {preview: "0.2.0"}) do
56+
run "shards install"
57+
58+
stdout = run "shards outdated --pre --no-color", capture: true
59+
assert_match "W: Outdated dependencies:", stdout
60+
assert_match " * preview (installed: 0.2.0, available: 0.4.0.a)", stdout
61+
end
62+
end
63+
end

0 commit comments

Comments
 (0)