libyear-bundler 0.3.0 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/libyear_bundler/bundle_outdated.rb +63 -0
- data/lib/libyear_bundler/calculators/libyear.rb +23 -0
- data/lib/libyear_bundler/calculators/version_number_delta.rb +46 -0
- data/lib/libyear_bundler/calculators/version_sequence_delta.rb +13 -0
- data/lib/libyear_bundler/cli.rb +52 -26
- data/lib/libyear_bundler/models/gem.rb +104 -0
- data/lib/libyear_bundler/models/ruby.rb +155 -0
- data/lib/libyear_bundler/options.rb +63 -0
- data/lib/libyear_bundler/report.rb +138 -19
- data/lib/libyear_bundler/version.rb +1 -1
- data/libyear-bundler.gemspec +18 -3
- metadata +46 -10
- data/.gitignore +0 -10
- data/CHANGELOG.md +0 -100
- data/Gemfile +0 -4
- data/README.md +0 -30
- data/lib/libyear_bundler/query.rb +0 -88
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 19cf55f9516aaf1b3ea4e870df4786d871844aec
|
4
|
+
data.tar.gz: 1f9ed94b7ce2d79bdebbb07fa8d59e2d7bfc6d5c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '09111fa6dbb524adb6f3a6af71f2fcd4803451f22e3d6d289cb61d8ef926553c781ddd3b801f33d7bbb7d957ea8fac52388982d752bb96625feedb3fd304aca2'
|
7
|
+
data.tar.gz: 7c9f3884523e18478563d45d5f35ef4f34f8b5d60d5fca78c369420a32dd3de6d3070e298347cc15d486c80405e357e0926b140abaf07e3d8d7a515dd664a91e
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "English"
|
2
|
+
require "open3"
|
3
|
+
require 'libyear_bundler/calculators/libyear'
|
4
|
+
require 'libyear_bundler/calculators/version_number_delta'
|
5
|
+
require 'libyear_bundler/calculators/version_sequence_delta'
|
6
|
+
require 'libyear_bundler/models/gem'
|
7
|
+
|
8
|
+
module LibyearBundler
|
9
|
+
# Responsible for getting all the data that goes into the `Report`.
|
10
|
+
class BundleOutdated
|
11
|
+
# Format of `bundle outdated --parseable` (BOP)
|
12
|
+
BOP_FMT = /\A(?<name>[^ ]+) \(newest (?<newest>[^,]+), installed (?<installed>[^,)]+)/
|
13
|
+
|
14
|
+
def initialize(gemfile_path)
|
15
|
+
@gemfile_path = gemfile_path
|
16
|
+
end
|
17
|
+
|
18
|
+
def execute
|
19
|
+
bundle_outdated.lines.each_with_object([]) do |line, gems|
|
20
|
+
match = BOP_FMT.match(line)
|
21
|
+
next if match.nil?
|
22
|
+
if malformed_version_strings?(match)
|
23
|
+
warn "Skipping #{match['name']} because of a malformed version string"
|
24
|
+
next
|
25
|
+
end
|
26
|
+
|
27
|
+
gem = ::LibyearBundler::Models::Gem.new(
|
28
|
+
match['name'],
|
29
|
+
match['installed'],
|
30
|
+
match['newest']
|
31
|
+
)
|
32
|
+
gems.push(gem)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def bundle_outdated
|
39
|
+
stdout, stderr, status = Open3.capture3(
|
40
|
+
%(BUNDLE_GEMFILE="#{@gemfile_path}" bundle outdated --parseable)
|
41
|
+
)
|
42
|
+
# Known statuses:
|
43
|
+
# 0 - Nothing is outdated
|
44
|
+
# 256 - Something is outdated
|
45
|
+
# 1792 - Unable to determine if something is outdated
|
46
|
+
unless [0, 256].include?(status.to_i)
|
47
|
+
$stderr.puts "`bundle outdated` failed with status: #{status.to_i}"
|
48
|
+
$stderr.puts "stderr: #{stderr}"
|
49
|
+
$stderr.puts "stdout: #{stdout}"
|
50
|
+
$stderr.puts "Try running `bundle install`."
|
51
|
+
Kernel.exit(CLI::E_BUNDLE_OUTDATED_FAILED)
|
52
|
+
end
|
53
|
+
stdout
|
54
|
+
end
|
55
|
+
|
56
|
+
# We rely on Gem::Version to handle version strings. If the string is malformed (usually because
|
57
|
+
# of a gem installed from git), then we won't be able to determine the dependency's freshness
|
58
|
+
def malformed_version_strings?(dependency)
|
59
|
+
!Gem::Version.correct?(dependency['installed']) ||
|
60
|
+
!Gem::Version.correct?(dependency['newest'])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module LibyearBundler
|
2
|
+
module Calculators
|
3
|
+
# A libyear is the difference in time between releases of the newest and
|
4
|
+
# installed versions of the gem in years
|
5
|
+
class Libyear
|
6
|
+
class << self
|
7
|
+
def calculate(installed_version_release_date, newest_version_release_date)
|
8
|
+
di = installed_version_release_date
|
9
|
+
dn = newest_version_release_date
|
10
|
+
if di.nil? || dn.nil? || dn <= di
|
11
|
+
# Known issue: Backports and maintenance releases of older minor versions.
|
12
|
+
# Example: json 1.8.6 (2017-01-13) was released *after* 2.0.3 (2017-01-12)
|
13
|
+
years = 0.0
|
14
|
+
else
|
15
|
+
days = (dn - di).to_f
|
16
|
+
years = days / 365.0
|
17
|
+
end
|
18
|
+
years
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module LibyearBundler
|
2
|
+
module Calculators
|
3
|
+
# The version number delta is the absolute difference between the highest-
|
4
|
+
# order version number of the installed and newest releases
|
5
|
+
class VersionNumberDelta
|
6
|
+
class << self
|
7
|
+
def calculate(installed_version, newest_version)
|
8
|
+
installed_version_tuple = version_tuple(installed_version.to_s.split('.'))
|
9
|
+
newest_version_tuple = version_tuple(newest_version.to_s.split('.'))
|
10
|
+
major_version_delta = version_delta(
|
11
|
+
newest_version_tuple.major, installed_version_tuple.major
|
12
|
+
)
|
13
|
+
minor_version_delta = version_delta(
|
14
|
+
newest_version_tuple.minor, installed_version_tuple.minor
|
15
|
+
)
|
16
|
+
patch_version_delta = version_delta(
|
17
|
+
newest_version_tuple.patch, installed_version_tuple.patch
|
18
|
+
)
|
19
|
+
highest_order([major_version_delta, minor_version_delta, patch_version_delta])
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def highest_order(arr)
|
25
|
+
arr[1] = arr[2] = 0 if arr[0] > 0
|
26
|
+
arr[2] = 0 if arr[1] > 0
|
27
|
+
arr
|
28
|
+
end
|
29
|
+
|
30
|
+
def version_delta(newest_version, installed_version)
|
31
|
+
delta = newest_version - installed_version
|
32
|
+
delta < 0 ? 0 : delta
|
33
|
+
end
|
34
|
+
|
35
|
+
def version_tuple(version_array)
|
36
|
+
version_struct = Struct.new(:major, :minor, :patch)
|
37
|
+
version_struct.new(
|
38
|
+
version_array[0].to_i,
|
39
|
+
version_array[1].to_i,
|
40
|
+
version_array[2].to_i
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module LibyearBundler
|
2
|
+
module Calculators
|
3
|
+
# The version sequence delta is the number of releases between the newest and
|
4
|
+
# installed versions of the gem
|
5
|
+
class VersionSequenceDelta
|
6
|
+
class << self
|
7
|
+
def calculate(installed_seq_index, newest_seq_index)
|
8
|
+
installed_seq_index - newest_seq_index
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/libyear_bundler/cli.rb
CHANGED
@@ -1,28 +1,31 @@
|
|
1
1
|
require "bundler/cli"
|
2
2
|
require "bundler/cli/outdated"
|
3
|
+
require "libyear_bundler/bundle_outdated"
|
4
|
+
require "libyear_bundler/options"
|
3
5
|
require "libyear_bundler/report"
|
4
|
-
require
|
6
|
+
require 'libyear_bundler/models/ruby'
|
5
7
|
|
6
8
|
module LibyearBundler
|
9
|
+
# The `libyear-bundler` command line program
|
7
10
|
class CLI
|
8
|
-
OPTIONS = %w(
|
9
|
-
--grand-total
|
10
|
-
).freeze
|
11
|
-
|
12
11
|
E_BUNDLE_OUTDATED_FAILED = 1
|
13
12
|
E_NO_GEMFILE = 2
|
13
|
+
E_INVALID_CLI_ARG = 3
|
14
14
|
|
15
15
|
def initialize(argv)
|
16
|
+
@options = ::LibyearBundler::Options.new(argv).parse
|
17
|
+
# Command line flags are removed form `argv` in `Options` by
|
18
|
+
# `OptionParser`, leaving non-flag command line arguments,
|
19
|
+
# such as a Gemfile path
|
16
20
|
@argv = argv
|
17
21
|
@gemfile_path = load_gemfile_path
|
18
|
-
validate_arguments
|
19
22
|
end
|
20
23
|
|
21
24
|
def run
|
22
|
-
if @
|
25
|
+
if @options.grand_total?
|
23
26
|
grand_total
|
24
27
|
else
|
25
|
-
print
|
28
|
+
print report.to_s
|
26
29
|
end
|
27
30
|
end
|
28
31
|
|
@@ -46,34 +49,57 @@ module LibyearBundler
|
|
46
49
|
'' # `bundle outdated` will default to local Gemfile
|
47
50
|
else
|
48
51
|
$stderr.puts "Gemfile not found"
|
49
|
-
exit
|
52
|
+
exit E_NO_GEMFILE
|
50
53
|
end
|
51
54
|
end
|
52
55
|
|
53
|
-
def
|
54
|
-
|
56
|
+
def bundle_outdated
|
57
|
+
BundleOutdated.new(@gemfile_path).execute
|
55
58
|
end
|
56
59
|
|
57
|
-
def
|
58
|
-
@
|
59
|
-
options = @argv.select { |arg| arg.start_with?("--") }
|
60
|
-
options.each_with_object([]) do |arg, memo|
|
61
|
-
memo << arg unless OPTIONS.include?(arg)
|
62
|
-
end
|
63
|
-
end
|
60
|
+
def report
|
61
|
+
@_report ||= Report.new(bundle_outdated, ruby, @options)
|
64
62
|
end
|
65
63
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
puts "Allowed args: #{OPTIONS.join(", ")}"
|
70
|
-
exit E_NO_GEMFILE
|
71
|
-
end
|
64
|
+
def ruby
|
65
|
+
lockfile = @gemfile_path + '.lock'
|
66
|
+
::LibyearBundler::Models::Ruby.new(lockfile)
|
72
67
|
end
|
73
68
|
|
74
69
|
def grand_total
|
75
|
-
|
76
|
-
|
70
|
+
puts calculate_grand_total
|
71
|
+
end
|
72
|
+
|
73
|
+
def calculate_grand_total
|
74
|
+
if [:libyears?, :releases?, :versions?].all? { |opt| @options[opt] }
|
75
|
+
[
|
76
|
+
libyears_grand_total,
|
77
|
+
releases_grand_total,
|
78
|
+
versions_grand_total
|
79
|
+
].join("\n")
|
80
|
+
elsif @options.releases?
|
81
|
+
releases_grand_total
|
82
|
+
elsif @options.versions?
|
83
|
+
versions_grand_total
|
84
|
+
else
|
85
|
+
libyears_grand_total
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def libyears_grand_total
|
90
|
+
report.to_h[:sum_libyears].truncate(1)
|
91
|
+
end
|
92
|
+
|
93
|
+
def releases_grand_total
|
94
|
+
report.to_h[:sum_seq_delta]
|
95
|
+
end
|
96
|
+
|
97
|
+
def versions_grand_total
|
98
|
+
[
|
99
|
+
report.to_h[:sum_major_version],
|
100
|
+
report.to_h[:sum_minor_version],
|
101
|
+
report.to_h[:sum_patch_version]
|
102
|
+
].to_s
|
77
103
|
end
|
78
104
|
end
|
79
105
|
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module LibyearBundler
|
6
|
+
module Models
|
7
|
+
# Logic and information pertaining to the installed and newest versions of
|
8
|
+
# a gem
|
9
|
+
class Gem
|
10
|
+
def initialize(name, installed_version, newest_version)
|
11
|
+
@name = name
|
12
|
+
@installed_version = installed_version
|
13
|
+
@newest_version = newest_version
|
14
|
+
end
|
15
|
+
|
16
|
+
def installed_version
|
17
|
+
::Gem::Version.new(@installed_version)
|
18
|
+
end
|
19
|
+
|
20
|
+
def installed_version_release_date
|
21
|
+
release_date(name, installed_version)
|
22
|
+
end
|
23
|
+
|
24
|
+
def installed_version_sequence_index
|
25
|
+
versions_sequence.index(installed_version.to_s)
|
26
|
+
end
|
27
|
+
|
28
|
+
def libyears
|
29
|
+
::LibyearBundler::Calculators::Libyear.calculate(
|
30
|
+
installed_version_release_date,
|
31
|
+
newest_version_release_date
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def name
|
36
|
+
@name
|
37
|
+
end
|
38
|
+
|
39
|
+
def newest_version
|
40
|
+
::Gem::Version.new(@newest_version)
|
41
|
+
end
|
42
|
+
|
43
|
+
def newest_version_sequence_index
|
44
|
+
versions_sequence.index(newest_version.to_s)
|
45
|
+
end
|
46
|
+
|
47
|
+
def newest_version_release_date
|
48
|
+
release_date(name, newest_version)
|
49
|
+
end
|
50
|
+
|
51
|
+
def version_number_delta
|
52
|
+
::LibyearBundler::Calculators::VersionNumberDelta.calculate(
|
53
|
+
installed_version,
|
54
|
+
newest_version
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
def version_sequence_delta
|
59
|
+
::LibyearBundler::Calculators::VersionSequenceDelta.calculate(
|
60
|
+
installed_version_sequence_index,
|
61
|
+
newest_version_sequence_index
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# docs: http://guides.rubygems.org/rubygems-org-api/#gem-version-methods
|
68
|
+
# Versions are returned ordered by version number, descending
|
69
|
+
def versions_sequence
|
70
|
+
@_versions_sequence ||= begin
|
71
|
+
uri = URI.parse("https://rubygems.org/api/v1/versions/#{name}.json")
|
72
|
+
response = Net::HTTP.get_response(uri)
|
73
|
+
parsed_response = JSON.parse(response.body)
|
74
|
+
parsed_response.map { |version| version['number'] }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Known issue: Probably performs a network request every time, unless
|
79
|
+
# there's some kind of caching.
|
80
|
+
def release_date(gem_name, gem_version)
|
81
|
+
dep = nil
|
82
|
+
begin
|
83
|
+
dep = ::Bundler::Dependency.new(gem_name, gem_version)
|
84
|
+
rescue ::Gem::Requirement::BadRequirementError => e
|
85
|
+
$stderr.puts "Could not find release date for: #{gem_name}"
|
86
|
+
$stderr.puts(e)
|
87
|
+
$stderr.puts(
|
88
|
+
"Maybe you used git in your Gemfile, which libyear doesn't support " \
|
89
|
+
"yet. Contributions welcome."
|
90
|
+
)
|
91
|
+
return nil
|
92
|
+
end
|
93
|
+
tuples, _errors = ::Gem::SpecFetcher.fetcher.search_for_dependency(dep)
|
94
|
+
if tuples.empty?
|
95
|
+
$stderr.puts "Could not find release date for: #{gem_name}"
|
96
|
+
return nil
|
97
|
+
end
|
98
|
+
tup, source = tuples.first # Gem::NameTuple
|
99
|
+
spec = source.fetch_spec(tup) # raises Gem::RemoteFetcher::FetchError
|
100
|
+
spec.date.to_date
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'bundler/lockfile_parser'
|
2
|
+
require 'bundler/ruby_version'
|
3
|
+
require 'date'
|
4
|
+
require 'net/http'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
require 'libyear_bundler/calculators/libyear'
|
8
|
+
require 'libyear_bundler/calculators/version_number_delta'
|
9
|
+
require 'libyear_bundler/calculators/version_sequence_delta'
|
10
|
+
|
11
|
+
module LibyearBundler
|
12
|
+
module Models
|
13
|
+
# Logic and information pertaining to the installed and newest Ruby versions
|
14
|
+
class Ruby
|
15
|
+
RUBY_VERSION_DATA_URL = "https://raw.githubusercontent.com/ruby/" \
|
16
|
+
"www.ruby-lang.org/master/_data/releases.yml".freeze
|
17
|
+
|
18
|
+
def initialize(lockfile)
|
19
|
+
@lockfile = lockfile
|
20
|
+
end
|
21
|
+
|
22
|
+
def installed_version
|
23
|
+
@_installed_version ||= begin
|
24
|
+
version_from_bundler ||
|
25
|
+
version_from_ruby_version_file ||
|
26
|
+
version_from_ruby
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def installed_version_release_date
|
31
|
+
release_date(installed_version.to_s)
|
32
|
+
end
|
33
|
+
|
34
|
+
def libyears
|
35
|
+
::LibyearBundler::Calculators::Libyear.calculate(
|
36
|
+
release_date(installed_version.to_s),
|
37
|
+
release_date(newest_version.to_s)
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def name
|
42
|
+
'ruby'
|
43
|
+
end
|
44
|
+
|
45
|
+
def newest_version
|
46
|
+
::Gem::Version.new(all_stable_versions.first)
|
47
|
+
end
|
48
|
+
|
49
|
+
def newest_version_release_date
|
50
|
+
release_date(newest_version.to_s)
|
51
|
+
end
|
52
|
+
|
53
|
+
def outdated?
|
54
|
+
installed_version < newest_version
|
55
|
+
end
|
56
|
+
|
57
|
+
def version_number_delta
|
58
|
+
::LibyearBundler::Calculators::VersionNumberDelta.calculate(
|
59
|
+
installed_version,
|
60
|
+
newest_version
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
def version_sequence_delta
|
65
|
+
::LibyearBundler::Calculators::VersionSequenceDelta.calculate(
|
66
|
+
installed_version_sequence_index,
|
67
|
+
newest_version_sequence_index
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
# The following URL is the only official, easily-parseable document with
|
74
|
+
# Ruby version information that I'm aware of, but is not supported as such
|
75
|
+
# (https://github.com/ruby/www.ruby-lang.org/pull/1637#issuecomment-344934173).
|
76
|
+
# It's been recommend that ruby-lang.org provide a supported document:
|
77
|
+
# https://github.com/ruby/www.ruby-lang.org/pull/1637#issuecomment-344934173
|
78
|
+
# TODO: Use supported document with version information if it becomes
|
79
|
+
# available.
|
80
|
+
def all_versions
|
81
|
+
@_all_versions ||= begin
|
82
|
+
uri = ::URI.parse(RUBY_VERSION_DATA_URL)
|
83
|
+
response = ::Net::HTTP.get_response(uri)
|
84
|
+
# The Date object is passed through here due to a bug in
|
85
|
+
# YAML#safe_load
|
86
|
+
# https://github.com/ruby/psych/issues/262
|
87
|
+
::YAML.safe_load(response.body, [Date])
|
88
|
+
.map { |release| release['version'] }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# We'll only consider non-prerelease versions when analyzing ruby version,
|
93
|
+
# which we also implcitly do for gem versions because that's bundler's
|
94
|
+
# default behavior
|
95
|
+
def all_stable_versions
|
96
|
+
all_versions.reject do |version|
|
97
|
+
::Gem::Version.new(version).prerelease?
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def installed_version_sequence_index
|
102
|
+
all_stable_versions.index(installed_version.to_s)
|
103
|
+
end
|
104
|
+
|
105
|
+
def newest_version_sequence_index
|
106
|
+
all_stable_versions.find_index(newest_version.to_s)
|
107
|
+
end
|
108
|
+
|
109
|
+
def release_date(version)
|
110
|
+
v = all_stable_versions.detect { |ver| ver == version }
|
111
|
+
|
112
|
+
if v.nil?
|
113
|
+
raise format('Cannot determine release date for ruby %s', version)
|
114
|
+
end
|
115
|
+
|
116
|
+
# YAML#safe_load provides an already-parsed Date object, so the following
|
117
|
+
# is a Date object
|
118
|
+
v['date']
|
119
|
+
end
|
120
|
+
|
121
|
+
def shell_out_to_ruby
|
122
|
+
# ruby appends a 'p' followed by the patch level number
|
123
|
+
# to the version number for stable releases, which returns
|
124
|
+
# a false positive using `::Gem::Version#prerelease?`.
|
125
|
+
# Understandably, because ruby is not a gem, but we'd like
|
126
|
+
# to use `prerelease?`.
|
127
|
+
# Pre-releases are appended with 'dev', and so adhere to
|
128
|
+
# `::Gem::Version`'s definition of a pre-release.
|
129
|
+
# Sources:
|
130
|
+
# - https://github.com/ruby/ruby/blob/trunk/version.h#L37
|
131
|
+
# - https://ruby-doc.org/stdlib-1.9.3/libdoc/rubygems/rdoc/Version.html
|
132
|
+
`ruby --version`.split[1].gsub(/p\d*/, '')
|
133
|
+
end
|
134
|
+
|
135
|
+
def version_from_bundler
|
136
|
+
return unless File.exist?(@lockfile)
|
137
|
+
ruby_version_string = ::Bundler::LockfileParser
|
138
|
+
.new(@lockfile)
|
139
|
+
.ruby_version
|
140
|
+
return if ruby_version_string.nil?
|
141
|
+
|
142
|
+
::Bundler::RubyVersion.from_string(ruby_version_string).gem_version
|
143
|
+
end
|
144
|
+
|
145
|
+
def version_from_ruby_version_file
|
146
|
+
return unless ::File.exist?('.ruby-version')
|
147
|
+
::Gem::Version.new(::File.read('.ruby-version').strip)
|
148
|
+
end
|
149
|
+
|
150
|
+
def version_from_ruby
|
151
|
+
::Gem::Version.new(shell_out_to_ruby)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'libyear_bundler/version'
|
3
|
+
require "libyear_bundler/cli"
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
module LibyearBundler
|
7
|
+
# Uses OptionParser from Ruby's stdlib to hand command-line arguments
|
8
|
+
class Options
|
9
|
+
BANNER = <<-BANNER.freeze
|
10
|
+
Usage: libyear-bundler [Gemfile ...] [options]
|
11
|
+
https://github.com/jaredbeck/libyear-bundler/
|
12
|
+
BANNER
|
13
|
+
|
14
|
+
def initialize(argv)
|
15
|
+
@argv = argv
|
16
|
+
@options = ::OpenStruct.new
|
17
|
+
@optparser = OptionParser.new do |opts|
|
18
|
+
opts.banner = BANNER
|
19
|
+
opts.program_name = 'libyear-bundler'
|
20
|
+
opts.version = ::LibyearBundler::VERSION
|
21
|
+
@options.send('libyears?=', true)
|
22
|
+
|
23
|
+
opts.on_head('-h', '--help', 'Prints this help') do
|
24
|
+
puts opts
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on('--all', 'Calculate all metrics') do
|
29
|
+
@options.send('libyears?=', true)
|
30
|
+
@options.send('releases?=', true)
|
31
|
+
@options.send('versions?=', true)
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on('--libyears', '[default] Calculate libyears out-of-date') do
|
35
|
+
@options.send('libyears?=', true)
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on('--releases', 'Calculate number of releases out-of-date') do
|
39
|
+
@options.send('libyears?=', false)
|
40
|
+
@options.send('releases?=', true)
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on('--versions', 'Calculate major, minor, and patch versions out-of-date') do
|
44
|
+
@options.send('libyears?=', false)
|
45
|
+
@options.send('versions?=', true)
|
46
|
+
end
|
47
|
+
|
48
|
+
opts.on('--grand-total', 'Return value for given metric(s)') do
|
49
|
+
@options.send('grand_total?=', true)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse
|
55
|
+
@optparser.parse!(@argv)
|
56
|
+
@options
|
57
|
+
rescue OptionParser::InvalidOption => e
|
58
|
+
warn e
|
59
|
+
warn @optparser.help
|
60
|
+
exit ::LibyearBundler::CLI::E_INVALID_CLI_ARG
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -1,30 +1,149 @@
|
|
1
1
|
module LibyearBundler
|
2
|
-
# Responsible presenting data from the
|
3
|
-
# with presentation, nothing else.
|
2
|
+
# Responsible presenting data from the `::LibyearBundler::Models`. Should only
|
3
|
+
# be concerned with presentation, nothing else.
|
4
4
|
class Report
|
5
|
-
|
6
|
-
|
5
|
+
FMT_LIBYEARS_COLUMN = "%10.1f".freeze
|
6
|
+
FMT_RELEASES_COLUMN = "%10d".freeze
|
7
|
+
FMT_VERSIONS_COLUMN = "%15s".freeze
|
8
|
+
FMT_SUMMARY_COLUMNS = "%30s%15s%15s%15s%15s".freeze
|
9
|
+
|
10
|
+
# `gems` - Array of `::LibyearBundler::Models::Gem` instances
|
11
|
+
# `options` - Instance of `::LibyearBundler::Options`
|
12
|
+
def initialize(gems, ruby, options)
|
7
13
|
@gems = gems
|
14
|
+
@ruby = ruby
|
15
|
+
@options = options
|
8
16
|
end
|
9
17
|
|
10
18
|
def to_s
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
to_h[:gems].each { |gem| put_line_summary(gem) }
|
20
|
+
|
21
|
+
begin
|
22
|
+
put_line_summary(@ruby) if @ruby.outdated?
|
23
|
+
rescue StandardError => e
|
24
|
+
warn "Unable to calculate libyears for ruby itself: #{e} (line summary)"
|
25
|
+
end
|
26
|
+
|
27
|
+
put_summary(to_h)
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_h
|
31
|
+
@_to_h ||=
|
32
|
+
begin
|
33
|
+
summary = {
|
34
|
+
gems: @gems,
|
35
|
+
sum_libyears: 0.0
|
36
|
+
}
|
37
|
+
@gems.each { |gem| increment_metrics_summary(gem, summary) }
|
38
|
+
|
39
|
+
begin
|
40
|
+
increment_metrics_summary(@ruby, summary) if @ruby.outdated?
|
41
|
+
rescue StandardError => e
|
42
|
+
warn "Unable to calculate libyears for ruby itself: #{e}"
|
43
|
+
end
|
44
|
+
|
45
|
+
summary
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def increment_metrics_summary(model, summary)
|
52
|
+
increment_libyears(model, summary) if @options.libyears?
|
53
|
+
increment_version_deltas(model, summary) if @options.versions?
|
54
|
+
increment_seq_deltas(model, summary) if @options.releases?
|
55
|
+
end
|
56
|
+
|
57
|
+
def put_line_summary(gem_or_ruby)
|
58
|
+
meta = meta_line_summary(gem_or_ruby)
|
59
|
+
|
60
|
+
if @options.releases?
|
61
|
+
releases = format(FMT_RELEASES_COLUMN, gem_or_ruby.version_sequence_delta)
|
62
|
+
meta << releases
|
63
|
+
end
|
64
|
+
|
65
|
+
if @options.versions?
|
66
|
+
versions = format(FMT_VERSIONS_COLUMN, gem_or_ruby.version_number_delta)
|
67
|
+
meta << versions
|
68
|
+
end
|
69
|
+
|
70
|
+
if @options.libyears?
|
71
|
+
libyears = format(FMT_LIBYEARS_COLUMN, gem_or_ruby.libyears)
|
72
|
+
meta << libyears
|
73
|
+
end
|
74
|
+
|
75
|
+
puts meta
|
76
|
+
end
|
77
|
+
|
78
|
+
def meta_line_summary(gem_or_ruby)
|
79
|
+
format(
|
80
|
+
FMT_SUMMARY_COLUMNS,
|
81
|
+
gem_or_ruby.name,
|
82
|
+
gem_or_ruby.installed_version.to_s,
|
83
|
+
gem_or_ruby.installed_version_release_date,
|
84
|
+
gem_or_ruby.newest_version.to_s,
|
85
|
+
gem_or_ruby.newest_version_release_date
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
def put_libyear_summary(sum_libyears)
|
90
|
+
puts format("System is %.1f libyears behind", sum_libyears)
|
91
|
+
end
|
92
|
+
|
93
|
+
def put_version_delta_summary(sum_major_version, sum_minor_version, sum_patch_version)
|
94
|
+
puts format(
|
95
|
+
"Major, minor, patch versions behind: %<major>d, %<minor>d, %<patch>d",
|
96
|
+
major: sum_major_version || 0,
|
97
|
+
minor: sum_minor_version || 0,
|
98
|
+
patch: sum_patch_version || 0
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
def put_sum_seq_delta_summary(sum_seq_delta)
|
103
|
+
puts format(
|
104
|
+
"Total releases behind: %<seq_delta>d",
|
105
|
+
seq_delta: sum_seq_delta || 0
|
106
|
+
)
|
107
|
+
end
|
108
|
+
|
109
|
+
def put_summary(summary)
|
110
|
+
if [:libyears?, :releases?, :versions?].all? { |opt| @options.send(opt) }
|
111
|
+
put_libyear_summary(summary[:sum_libyears])
|
112
|
+
put_sum_seq_delta_summary(summary[:sum_seq_delta])
|
113
|
+
put_version_delta_summary(
|
114
|
+
summary[:sum_major_version],
|
115
|
+
summary[:sum_minor_version],
|
116
|
+
summary[:sum_patch_version]
|
117
|
+
)
|
118
|
+
elsif @options.versions?
|
119
|
+
put_version_delta_summary(
|
120
|
+
summary[:sum_major_version],
|
121
|
+
summary[:sum_minor_version],
|
122
|
+
summary[:sum_patch_version]
|
25
123
|
)
|
124
|
+
elsif @options.releases?
|
125
|
+
put_sum_seq_delta_summary(summary[:sum_seq_delta])
|
126
|
+
elsif @options.libyears?
|
127
|
+
put_libyear_summary(summary[:sum_libyears])
|
26
128
|
end
|
27
|
-
|
129
|
+
end
|
130
|
+
|
131
|
+
def increment_libyears(model, memo)
|
132
|
+
memo[:sum_libyears] += model.libyears
|
133
|
+
end
|
134
|
+
|
135
|
+
def increment_seq_deltas(model, memo)
|
136
|
+
memo[:sum_seq_delta] ||= 0
|
137
|
+
memo[:sum_seq_delta] += model.version_sequence_delta
|
138
|
+
end
|
139
|
+
|
140
|
+
def increment_version_deltas(model, memo)
|
141
|
+
memo[:sum_major_version] ||= 0
|
142
|
+
memo[:sum_major_version] += model.version_number_delta[0]
|
143
|
+
memo[:sum_minor_version] ||= 0
|
144
|
+
memo[:sum_minor_version] += model.version_number_delta[1]
|
145
|
+
memo[:sum_patch_version] ||= 0
|
146
|
+
memo[:sum_patch_version] += model.version_number_delta[2]
|
28
147
|
end
|
29
148
|
end
|
30
149
|
end
|
data/libyear-bundler.gemspec
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
|
2
3
|
lib = File.expand_path('../lib', __FILE__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
5
|
require 'libyear_bundler/version'
|
@@ -11,12 +12,26 @@ Gem::Specification.new do |spec|
|
|
11
12
|
spec.summary = "A simple measure of dependency freshness"
|
12
13
|
spec.homepage = "https://libyear.com"
|
13
14
|
spec.licenses = ["GPL-3.0"]
|
14
|
-
spec.files = `git ls-files -z`.split("\x0").
|
15
|
-
f.
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").select do |f|
|
16
|
+
f.start_with?('lib/') ||
|
17
|
+
[
|
18
|
+
'bin/libyear-bundler',
|
19
|
+
'libyear-bundler.gemspec',
|
20
|
+
'LICENSE.txt'
|
21
|
+
].include?(f)
|
16
22
|
end
|
17
23
|
spec.bindir = "bin"
|
18
24
|
spec.executables = ["libyear-bundler"]
|
19
25
|
spec.require_paths = ["lib"]
|
20
26
|
spec.required_ruby_version = ">= 2.1"
|
21
|
-
|
27
|
+
|
28
|
+
# We will support bundler 1 as long as we can. It's important that people
|
29
|
+
# with badly out-of-date systems can still measure how bad they are.
|
30
|
+
spec.add_dependency "bundler", ">= 1.14", "< 3"
|
31
|
+
|
32
|
+
spec.add_development_dependency "rspec", "~> 3.9"
|
33
|
+
|
34
|
+
# rubocop 0.57 drops support for ruby 2.1, so 0.56 is the highest we can
|
35
|
+
# use. We're going to support ruby 2.1 as long as possible. See above.
|
36
|
+
spec.add_development_dependency "rubocop", "~> 0.56.0"
|
22
37
|
end
|
metadata
CHANGED
@@ -1,29 +1,63 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: libyear-bundler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3
|
4
|
+
version: 0.5.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jared Beck
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-06-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.14'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '3'
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
|
-
- - "
|
27
|
+
- - ">="
|
25
28
|
- !ruby/object:Gem::Version
|
26
29
|
version: '1.14'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '3'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rspec
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '3.9'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '3.9'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rubocop
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.56.0
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 0.56.0
|
27
61
|
description:
|
28
62
|
email:
|
29
63
|
- jared@jaredbeck.com
|
@@ -32,15 +66,17 @@ executables:
|
|
32
66
|
extensions: []
|
33
67
|
extra_rdoc_files: []
|
34
68
|
files:
|
35
|
-
- ".gitignore"
|
36
|
-
- CHANGELOG.md
|
37
|
-
- Gemfile
|
38
69
|
- LICENSE.txt
|
39
|
-
- README.md
|
40
70
|
- bin/libyear-bundler
|
41
71
|
- lib/libyear_bundler.rb
|
72
|
+
- lib/libyear_bundler/bundle_outdated.rb
|
73
|
+
- lib/libyear_bundler/calculators/libyear.rb
|
74
|
+
- lib/libyear_bundler/calculators/version_number_delta.rb
|
75
|
+
- lib/libyear_bundler/calculators/version_sequence_delta.rb
|
42
76
|
- lib/libyear_bundler/cli.rb
|
43
|
-
- lib/libyear_bundler/
|
77
|
+
- lib/libyear_bundler/models/gem.rb
|
78
|
+
- lib/libyear_bundler/models/ruby.rb
|
79
|
+
- lib/libyear_bundler/options.rb
|
44
80
|
- lib/libyear_bundler/report.rb
|
45
81
|
- lib/libyear_bundler/version.rb
|
46
82
|
- libyear-bundler.gemspec
|
@@ -64,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
100
|
version: '0'
|
65
101
|
requirements: []
|
66
102
|
rubyforge_project:
|
67
|
-
rubygems_version: 2.
|
103
|
+
rubygems_version: 2.5.2.3
|
68
104
|
signing_key:
|
69
105
|
specification_version: 4
|
70
106
|
summary: A simple measure of dependency freshness
|
data/.gitignore
DELETED
data/CHANGELOG.md
DELETED
@@ -1,100 +0,0 @@
|
|
1
|
-
# libyear
|
2
|
-
|
3
|
-
This project follows [semver 2.0.0][1] and the recommendations
|
4
|
-
of [keepachangelog.com][2].
|
5
|
-
|
6
|
-
## Unreleased
|
7
|
-
|
8
|
-
Breaking changes:
|
9
|
-
|
10
|
-
- None
|
11
|
-
|
12
|
-
Added:
|
13
|
-
|
14
|
-
- None
|
15
|
-
|
16
|
-
Fixed:
|
17
|
-
|
18
|
-
- None
|
19
|
-
|
20
|
-
## 0.3.0 (2017-03-24)
|
21
|
-
|
22
|
-
Breaking changes:
|
23
|
-
|
24
|
-
- None
|
25
|
-
|
26
|
-
Added:
|
27
|
-
|
28
|
-
- [#1](https://github.com/jaredbeck/libyear-bundler/pull/1)
|
29
|
-
Add --grand-total option
|
30
|
-
|
31
|
-
Fixed:
|
32
|
-
|
33
|
-
- None
|
34
|
-
|
35
|
-
## 0.2.0 (2017-03-10)
|
36
|
-
|
37
|
-
Breaking changes:
|
38
|
-
|
39
|
-
- Rename project
|
40
|
-
- Rename project from libyear-rb to libyear-bundler
|
41
|
-
- Rename binary from libyear to libyear-bundler
|
42
|
-
- Discussion: https://github.com/jaredbeck/libyear-rb/issues/1
|
43
|
-
|
44
|
-
Added:
|
45
|
-
|
46
|
-
- None
|
47
|
-
|
48
|
-
Fixed:
|
49
|
-
|
50
|
-
- None
|
51
|
-
|
52
|
-
## 0.1.3 (2017-03-07)
|
53
|
-
|
54
|
-
Breaking changes:
|
55
|
-
|
56
|
-
- None
|
57
|
-
|
58
|
-
Added:
|
59
|
-
|
60
|
-
- None
|
61
|
-
|
62
|
-
Fixed:
|
63
|
-
|
64
|
-
- Don't crash when Gemfile uses git
|
65
|
-
|
66
|
-
## 0.1.2 (2017-02-16)
|
67
|
-
|
68
|
-
Breaking changes:
|
69
|
-
|
70
|
-
- None
|
71
|
-
|
72
|
-
Added:
|
73
|
-
|
74
|
-
- None
|
75
|
-
|
76
|
-
Fixed:
|
77
|
-
|
78
|
-
- Better handling of weird sources like rails-assets
|
79
|
-
- Wider report columns
|
80
|
-
|
81
|
-
## 0.1.1 (2017-02-14)
|
82
|
-
|
83
|
-
Breaking changes:
|
84
|
-
|
85
|
-
- None
|
86
|
-
|
87
|
-
Added:
|
88
|
-
|
89
|
-
- None
|
90
|
-
|
91
|
-
Fixed:
|
92
|
-
|
93
|
-
- Better handling of error when bundle outdated fails
|
94
|
-
|
95
|
-
## 0.1.0 (2017-02-13)
|
96
|
-
|
97
|
-
Initial version. Proof of concept.
|
98
|
-
|
99
|
-
[1]: http://semver.org/spec/v2.0.0.html
|
100
|
-
[2]: http://keepachangelog.com/
|
data/Gemfile
DELETED
data/README.md
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
# Libyear
|
2
|
-
|
3
|
-
A simple measure of dependency freshness for ruby apps.
|
4
|
-
|
5
|
-
A libyear (library year) is a measure of how old a software dependency is.
|
6
|
-
|
7
|
-
If your system has two dependencies, the first one year old, the second three,
|
8
|
-
then your system is four libyears out-of-date.
|
9
|
-
|
10
|
-
A dependency is one year old when the version you are using is one year older
|
11
|
-
than its latest version.
|
12
|
-
|
13
|
-
## Usage
|
14
|
-
|
15
|
-
Early access. Output and usage subject to change.
|
16
|
-
|
17
|
-
```
|
18
|
-
gem install libyear-bundler
|
19
|
-
libyear-bundler Gemfile
|
20
|
-
activesupport 4.2.7.1 2016-08-10 5.0.1 2016-12-21 0.4
|
21
|
-
json 1.8.6 2017-01-13 2.0.3 2017-01-12 0.0
|
22
|
-
minitest_to_rspec 0.6.0 2015-06-09 0.8.0 2017-01-02 1.6
|
23
|
-
System is 1.9 libyears behind
|
24
|
-
```
|
25
|
-
|
26
|
-
## Development
|
27
|
-
|
28
|
-
```
|
29
|
-
ruby -I lib bin/libyear-bundler spec/fixtures/01/Gemfile
|
30
|
-
```
|
@@ -1,88 +0,0 @@
|
|
1
|
-
require "English"
|
2
|
-
require "open3"
|
3
|
-
|
4
|
-
module LibyearBundler
|
5
|
-
# Responsible for getting all the data that goes into the `Report`.
|
6
|
-
class Query
|
7
|
-
# Format of `bundle outdated --parseable` (BOP)
|
8
|
-
BOP_FMT = /\A(?<name>[^ ]+) \(newest (?<newest>[^,]+), installed (?<installed>[^,)]+)/
|
9
|
-
|
10
|
-
def initialize(gemfile_path)
|
11
|
-
@gemfile_path = gemfile_path
|
12
|
-
end
|
13
|
-
|
14
|
-
def execute
|
15
|
-
gems = []
|
16
|
-
bundle_outdated.lines.each do |line|
|
17
|
-
match = BOP_FMT.match(line)
|
18
|
-
next if match.nil?
|
19
|
-
gems.push(
|
20
|
-
installed: { version: match["installed"] },
|
21
|
-
name: match["name"],
|
22
|
-
newest: { version: match["newest"] }
|
23
|
-
)
|
24
|
-
end
|
25
|
-
gems.each do |gem|
|
26
|
-
di = release_date(gem[:name], gem[:installed][:version])
|
27
|
-
dn = release_date(gem[:name], gem[:newest][:version])
|
28
|
-
gem[:installed][:date] = di
|
29
|
-
gem[:newest][:date] = dn
|
30
|
-
if di.nil? || dn.nil? || dn <= di
|
31
|
-
# Known issue: Backports and maintenance releases of older minor versions.
|
32
|
-
# Example: json 1.8.6 (2017-01-13) was released *after* 2.0.3 (2017-01-12)
|
33
|
-
years = 0.0
|
34
|
-
else
|
35
|
-
days = (dn - di).to_f
|
36
|
-
years = days / 365.0
|
37
|
-
end
|
38
|
-
gem[:libyears] = years
|
39
|
-
end
|
40
|
-
gems
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def bundle_outdated
|
46
|
-
stdout, stderr, status = Open3.capture3(
|
47
|
-
%Q(BUNDLE_GEMFILE="#{@gemfile_path}" bundle outdated --parseable)
|
48
|
-
)
|
49
|
-
# Known statuses:
|
50
|
-
# 0 - Nothing is outdated
|
51
|
-
# 256 - Something is outdated
|
52
|
-
# 1792 - Unable to determine if something is outdated
|
53
|
-
unless [0, 256].include?(status.to_i)
|
54
|
-
$stderr.puts "`bundle outdated` failed with status: #{status.to_i}"
|
55
|
-
$stderr.puts "stderr: #{stderr}"
|
56
|
-
$stderr.puts "stdout: #{stdout}"
|
57
|
-
$stderr.puts "Try running `bundle install`."
|
58
|
-
Kernel.exit(CLI::E_BUNDLE_OUTDATED_FAILED)
|
59
|
-
end
|
60
|
-
stdout
|
61
|
-
end
|
62
|
-
|
63
|
-
# Known issue: Probably performs a network request every time, unless
|
64
|
-
# there's some kind of caching.
|
65
|
-
def release_date(gem_name, gem_version)
|
66
|
-
dep = nil
|
67
|
-
begin
|
68
|
-
dep = ::Bundler::Dependency.new(gem_name, gem_version)
|
69
|
-
rescue ::Gem::Requirement::BadRequirementError => e
|
70
|
-
$stderr.puts "Could not find release date for: #{gem_name}"
|
71
|
-
$stderr.puts(e)
|
72
|
-
$stderr.puts(
|
73
|
-
"Maybe you used git in your Gemfile, which libyear doesn't support " \
|
74
|
-
"yet. Contributions welcome."
|
75
|
-
)
|
76
|
-
return nil
|
77
|
-
end
|
78
|
-
tuples, _errors = ::Gem::SpecFetcher.fetcher.search_for_dependency(dep)
|
79
|
-
if tuples.empty?
|
80
|
-
$stderr.puts "Could not find release date for: #{gem_name}"
|
81
|
-
return nil
|
82
|
-
end
|
83
|
-
tup, source = tuples.first # Gem::NameTuple
|
84
|
-
spec = source.fetch_spec(tup) # raises Gem::RemoteFetcher::FetchError
|
85
|
-
spec.date.to_date
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|