libyear-bundler 0.3.0 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|