libyear-bundler 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 19cf55f9516aaf1b3ea4e870df4786d871844aec
4
- data.tar.gz: 1f9ed94b7ce2d79bdebbb07fa8d59e2d7bfc6d5c
2
+ SHA256:
3
+ metadata.gz: 6605cda8b92cd002e2b1a536a716ba50d8c0df4b550db28f6b0be4760d7734f7
4
+ data.tar.gz: eec844bd3aaf31c95fc29827ca6a33166da392ab799a03b3b3b5269cc5507fe7
5
5
  SHA512:
6
- metadata.gz: '09111fa6dbb524adb6f3a6af71f2fcd4803451f22e3d6d289cb61d8ef926553c781ddd3b801f33d7bbb7d957ea8fac52388982d752bb96625feedb3fd304aca2'
7
- data.tar.gz: 7c9f3884523e18478563d45d5f35ef4f34f8b5d60d5fca78c369420a32dd3de6d3070e298347cc15d486c80405e357e0926b140abaf07e3d8d7a515dd664a91e
6
+ metadata.gz: 555318739eb3506de90e6d21f103fbb8ce2c0bd4225595830465c0aae476a906d55ffcf534e375925146c695348bbca975fe75ef962752bb4d41f474b6308b2d
7
+ data.tar.gz: a1fe20a4d14b170ccc8b10949868cf859382135651ec799bd1c0bfff4c588a5b92e63d7bf347cc0987feb4aa8199966e986b56d797d30828e9b01b3fb64cde72
@@ -11,8 +11,9 @@ module LibyearBundler
11
11
  # Format of `bundle outdated --parseable` (BOP)
12
12
  BOP_FMT = /\A(?<name>[^ ]+) \(newest (?<newest>[^,]+), installed (?<installed>[^,)]+)/
13
13
 
14
- def initialize(gemfile_path)
14
+ def initialize(gemfile_path, release_date_cache)
15
15
  @gemfile_path = gemfile_path
16
+ @release_date_cache = release_date_cache
16
17
  end
17
18
 
18
19
  def execute
@@ -27,7 +28,8 @@ module LibyearBundler
27
28
  gem = ::LibyearBundler::Models::Gem.new(
28
29
  match['name'],
29
30
  match['installed'],
30
- match['newest']
31
+ match['newest'],
32
+ @release_date_cache
31
33
  )
32
34
  gems.push(gem)
33
35
  end
@@ -2,6 +2,7 @@ require "bundler/cli"
2
2
  require "bundler/cli/outdated"
3
3
  require "libyear_bundler/bundle_outdated"
4
4
  require "libyear_bundler/options"
5
+ require "libyear_bundler/release_date_cache"
5
6
  require "libyear_bundler/report"
6
7
  require 'libyear_bundler/models/ruby'
7
8
 
@@ -27,6 +28,12 @@ module LibyearBundler
27
28
  else
28
29
  print report.to_s
29
30
  end
31
+
32
+ # Update cache
33
+ cache_path = @options.cache_path
34
+ if cache_path && release_date_cache
35
+ release_date_cache.save(cache_path)
36
+ end
30
37
  end
31
38
 
32
39
  private
@@ -54,7 +61,15 @@ module LibyearBundler
54
61
  end
55
62
 
56
63
  def bundle_outdated
57
- BundleOutdated.new(@gemfile_path).execute
64
+ BundleOutdated.new(@gemfile_path, release_date_cache).execute
65
+ end
66
+
67
+ def release_date_cache
68
+ @_release_date_cache ||= begin
69
+ path = @options.cache_path
70
+ return if path.nil?
71
+ ReleaseDateCache.load(path)
72
+ end
58
73
  end
59
74
 
60
75
  def report
@@ -63,7 +78,7 @@ module LibyearBundler
63
78
 
64
79
  def ruby
65
80
  lockfile = @gemfile_path + '.lock'
66
- ::LibyearBundler::Models::Ruby.new(lockfile)
81
+ ::LibyearBundler::Models::Ruby.new(lockfile, release_date_cache)
67
82
  end
68
83
 
69
84
  def grand_total
@@ -7,10 +7,39 @@ module LibyearBundler
7
7
  # Logic and information pertaining to the installed and newest versions of
8
8
  # a gem
9
9
  class Gem
10
- def initialize(name, installed_version, newest_version)
10
+ def initialize(name, installed_version, newest_version, release_date_cache)
11
+ unless release_date_cache.nil? || release_date_cache.is_a?(ReleaseDateCache)
12
+ raise TypeError, 'Invalid release_date_cache'
13
+ end
11
14
  @name = name
12
15
  @installed_version = installed_version
13
16
  @newest_version = newest_version
17
+ @release_date_cache = release_date_cache
18
+ end
19
+
20
+ class << self
21
+ def release_date(gem_name, gem_version)
22
+ dep = nil
23
+ begin
24
+ dep = ::Bundler::Dependency.new(gem_name, gem_version)
25
+ rescue ::Gem::Requirement::BadRequirementError => e
26
+ $stderr.puts "Could not find release date for: #{gem_name}"
27
+ $stderr.puts(e)
28
+ $stderr.puts(
29
+ "Maybe you used git in your Gemfile, which libyear doesn't support " \
30
+ "yet. Contributions welcome."
31
+ )
32
+ return nil
33
+ end
34
+ tuples, _errors = ::Gem::SpecFetcher.fetcher.search_for_dependency(dep)
35
+ if tuples.empty?
36
+ $stderr.puts "Could not find release date for: #{gem_name}"
37
+ return nil
38
+ end
39
+ tup, source = tuples.first # Gem::NameTuple
40
+ spec = source.fetch_spec(tup) # raises Gem::RemoteFetcher::FetchError
41
+ spec.date.to_date
42
+ end
14
43
  end
15
44
 
16
45
  def installed_version
@@ -18,7 +47,11 @@ module LibyearBundler
18
47
  end
19
48
 
20
49
  def installed_version_release_date
21
- release_date(name, installed_version)
50
+ if @release_date_cache.nil?
51
+ self.class.release_date(name, installed_version)
52
+ else
53
+ @release_date_cache[name, installed_version]
54
+ end
22
55
  end
23
56
 
24
57
  def installed_version_sequence_index
@@ -45,7 +78,11 @@ module LibyearBundler
45
78
  end
46
79
 
47
80
  def newest_version_release_date
48
- release_date(name, newest_version)
81
+ if @release_date_cache.nil?
82
+ self.class.release_date(name, newest_version)
83
+ else
84
+ @release_date_cache[name, newest_version]
85
+ end
49
86
  end
50
87
 
51
88
  def version_number_delta
@@ -74,31 +111,6 @@ module LibyearBundler
74
111
  parsed_response.map { |version| version['number'] }
75
112
  end
76
113
  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
114
  end
103
115
  end
104
116
  end
@@ -15,8 +15,78 @@ module LibyearBundler
15
15
  RUBY_VERSION_DATA_URL = "https://raw.githubusercontent.com/ruby/" \
16
16
  "www.ruby-lang.org/master/_data/releases.yml".freeze
17
17
 
18
- def initialize(lockfile)
18
+ def initialize(lockfile, release_date_cache)
19
+ unless release_date_cache.nil? || release_date_cache.is_a?(ReleaseDateCache)
20
+ raise TypeError, 'Invalid release_date_cache'
21
+ end
19
22
  @lockfile = lockfile
23
+ @release_date_cache = release_date_cache
24
+ end
25
+
26
+ class << self
27
+ # We'll only consider non-prerelease versions when analyzing ruby version,
28
+ # which we also implcitly do for gem versions because that's bundler's
29
+ # default behavior
30
+ #
31
+ # @return [Array<String>]
32
+ def all_stable_versions
33
+ all_versions.reject do |version|
34
+ ::Gem::Version.new(version).prerelease?
35
+ end
36
+ end
37
+
38
+ def newest_version
39
+ ::Gem::Version.new(all_stable_versions.first)
40
+ end
41
+
42
+ def newest_version_release_date
43
+ if @release_date_cache.nil?
44
+ release_date(newest_version)
45
+ else
46
+ @release_date_cache[name, newest_version]
47
+ end
48
+ end
49
+
50
+ def newest_version_sequence_index
51
+ all_stable_versions.find_index(newest_version.to_s)
52
+ end
53
+
54
+ def release_date(version_obj)
55
+ version = version_obj.to_s
56
+ v = all_stable_versions.detect { |ver| ver == version }
57
+
58
+ if v.nil?
59
+ raise format('Cannot determine release date for ruby %s', version)
60
+ end
61
+
62
+ # YAML#safe_load provides an already-parsed Date object, so the following
63
+ # is a Date object
64
+ v['date']
65
+ end
66
+
67
+ private
68
+
69
+ # The following URL is the only official, easily-parseable document with
70
+ # Ruby version information that I'm aware of, but is not supported as such
71
+ # (https://github.com/ruby/www.ruby-lang.org/pull/1637#issuecomment-344934173).
72
+ # It's been recommend that ruby-lang.org provide a supported document:
73
+ # https://github.com/ruby/www.ruby-lang.org/pull/1637#issuecomment-344934173
74
+ # TODO: Use supported document with version information if it becomes
75
+ # available.
76
+ #
77
+ # @return [Array<String>]
78
+ def all_versions
79
+ @_all_versions ||= begin
80
+ uri = ::URI.parse(RUBY_VERSION_DATA_URL)
81
+ response = ::Net::HTTP.get_response(uri)
82
+ # The Date object is passed through here due to a bug in
83
+ # YAML#safe_load
84
+ # https://github.com/ruby/psych/issues/262
85
+ ::YAML
86
+ .safe_load(response.body, [Date])
87
+ .map { |release| release['version'] }
88
+ end
89
+ end
20
90
  end
21
91
 
22
92
  def installed_version
@@ -28,13 +98,17 @@ module LibyearBundler
28
98
  end
29
99
 
30
100
  def installed_version_release_date
31
- release_date(installed_version.to_s)
101
+ if @release_date_cache.nil?
102
+ self.class.release_date(installed_version)
103
+ else
104
+ @release_date_cache[name, installed_version]
105
+ end
32
106
  end
33
107
 
34
108
  def libyears
35
109
  ::LibyearBundler::Calculators::Libyear.calculate(
36
- release_date(installed_version.to_s),
37
- release_date(newest_version.to_s)
110
+ installed_version_release_date,
111
+ self.class.newest_version_release_date
38
112
  )
39
113
  end
40
114
 
@@ -42,12 +116,16 @@ module LibyearBundler
42
116
  'ruby'
43
117
  end
44
118
 
119
+ # Simply delegates to class method, but we still need it to conform to
120
+ # the interface expected by `Report#meta_line_summary`.
45
121
  def newest_version
46
- ::Gem::Version.new(all_stable_versions.first)
122
+ self.class.newest_version
47
123
  end
48
124
 
125
+ # Simply delegates to class method, but we still need it to conform to
126
+ # the interface expected by `Report#meta_line_summary`.
49
127
  def newest_version_release_date
50
- release_date(newest_version.to_s)
128
+ self.class.newest_version_release_date
51
129
  end
52
130
 
53
131
  def outdated?
@@ -57,65 +135,21 @@ module LibyearBundler
57
135
  def version_number_delta
58
136
  ::LibyearBundler::Calculators::VersionNumberDelta.calculate(
59
137
  installed_version,
60
- newest_version
138
+ self.class.newest_version
61
139
  )
62
140
  end
63
141
 
64
142
  def version_sequence_delta
65
143
  ::LibyearBundler::Calculators::VersionSequenceDelta.calculate(
66
144
  installed_version_sequence_index,
67
- newest_version_sequence_index
145
+ self.class.newest_version_sequence_index
68
146
  )
69
147
  end
70
148
 
71
149
  private
72
150
 
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
151
  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']
152
+ self.class.all_stable_versions.index(installed_version.to_s)
119
153
  end
120
154
 
121
155
  def shell_out_to_ruby
@@ -142,6 +176,8 @@ module LibyearBundler
142
176
  ::Bundler::RubyVersion.from_string(ruby_version_string).gem_version
143
177
  end
144
178
 
179
+ # TODO: this path should probably be relative to `@lockfile` instead
180
+ # TODO: of being relative to the current working directory.
145
181
  def version_from_ruby_version_file
146
182
  return unless ::File.exist?('.ruby-version')
147
183
  ::Gem::Version.new(::File.read('.ruby-version').strip)
@@ -31,6 +31,10 @@ https://github.com/jaredbeck/libyear-bundler/
31
31
  @options.send('versions?=', true)
32
32
  end
33
33
 
34
+ opts.on('--cache=CACHE_PATH', 'Use a cache across runs') do |cache_path|
35
+ @options.cache_path = cache_path
36
+ end
37
+
34
38
  opts.on('--libyears', '[default] Calculate libyears out-of-date') do
35
39
  @options.send('libyears?=', true)
36
40
  end
@@ -0,0 +1,50 @@
1
+ require 'yaml'
2
+
3
+ module LibyearBundler
4
+ # A cache of release dates by name and version, for both gems and rubies.
5
+ class ReleaseDateCache
6
+ # @param data [Hash<String,Date>]
7
+ def initialize(data)
8
+ raise TypeError unless data.is_a?(Hash)
9
+ @data = data
10
+ end
11
+
12
+ def [](name, version)
13
+ key = format('%s-%s', name, version)
14
+ if @data.key?(key)
15
+ @data[key]
16
+ else
17
+ @data[key] = release_date(name, version)
18
+ end
19
+ end
20
+
21
+ class << self
22
+ def load(path)
23
+ if File.exist?(path)
24
+ new(YAML.safe_load(File.read(path), [Date]))
25
+ else
26
+ new({})
27
+ end
28
+ end
29
+ end
30
+
31
+ def save(path)
32
+ content = YAML.dump(@data)
33
+ begin
34
+ File.write(path, content)
35
+ rescue StandardError => e
36
+ warn format('Unable to update cache: %s, %s', path, e.message)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def release_date(name, version)
43
+ if name == 'ruby'
44
+ Models::Ruby.release_date(version)
45
+ else
46
+ Models::Gem.release_date(name, version)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,3 +1,3 @@
1
1
  module LibyearBundler
2
- VERSION = "0.5.3".freeze
2
+ VERSION = "0.6.0".freeze
3
3
  end
@@ -23,10 +23,14 @@ Gem::Specification.new do |spec|
23
23
  spec.bindir = "bin"
24
24
  spec.executables = ["libyear-bundler"]
25
25
  spec.require_paths = ["lib"]
26
+
27
+ # We deliberately support dead rubies, as long as possible. It's important
28
+ # that people with badly out-of-date systems can still measure how bad they
29
+ # are.
26
30
  spec.required_ruby_version = ">= 2.1"
27
31
 
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.
32
+ # We will support bundler 1 as long as we can. See `required_ruby_version`
33
+ # above.
30
34
  spec.add_dependency "bundler", ">= 1.14", "< 3"
31
35
 
32
36
  spec.add_development_dependency "rspec", "~> 3.9"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: libyear-bundler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jared Beck
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-26 00:00:00.000000000 Z
11
+ date: 2021-08-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -77,6 +77,7 @@ files:
77
77
  - lib/libyear_bundler/models/gem.rb
78
78
  - lib/libyear_bundler/models/ruby.rb
79
79
  - lib/libyear_bundler/options.rb
80
+ - lib/libyear_bundler/release_date_cache.rb
80
81
  - lib/libyear_bundler/report.rb
81
82
  - lib/libyear_bundler/version.rb
82
83
  - libyear-bundler.gemspec
@@ -100,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
100
101
  version: '0'
101
102
  requirements: []
102
103
  rubyforge_project:
103
- rubygems_version: 2.5.2.3
104
+ rubygems_version: 2.7.6.3
104
105
  signing_key:
105
106
  specification_version: 4
106
107
  summary: A simple measure of dependency freshness