gem_updater 3.0.0 → 4.4.1

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
2
  SHA256:
3
- metadata.gz: 458c286b02adc73fdeac3eded5de4b65390c1bfa0374efc83651b773f96f2f7b
4
- data.tar.gz: bf55e5150ee714581df3b41e4177878c2b432d44c369d8bdd1cf62ae8f435d21
3
+ metadata.gz: 10e740d324e4bd91b5e7b8d6c7d2c4272b84eedf0cabcab2e5244c55779a4a2c
4
+ data.tar.gz: d7050c27a7791a3b185c08e088e29c9cf645908106d352027d3ddb02f826bdae
5
5
  SHA512:
6
- metadata.gz: be83c20442e904d34c99f3674532ac80ff891e7a088abfe7f9d7867bad4c2208066ac57fd6861c508d3d47624a0f7946f72e48cb8d99abe4272e6aa3e7554287
7
- data.tar.gz: 769a8fcbbe5b19d9e9c334079e5c3a84c1fbf0d4688aace73ee33b808ee8d4fd3728247b617985694b80365715a3787768b3a24a5d9ba10c811e8bf1b0b4f692
6
+ metadata.gz: 26be9e96400f04d892de240c28af03f02d5e5d23ec33d51bdbce015883df6bc4de2a1631da3ab42b1bada40384868a095f1b1e2c1a82fe1f6ca7cbd231656864
7
+ data.tar.gz: 55a53a16a210ef6380ca2bc3febafaf01ecd42de0693fa88fcabfe7302e195d9fe0ba69989bcc37b9638f085cb06a42fe33a3c78e1e691bb4557f291b043e90a
data/bin/gem_update CHANGED
@@ -1,10 +1,13 @@
1
1
  #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
2
5
  require 'optparse'
3
6
 
4
7
  # Exit cleanly from an early interrupt
5
8
  Signal.trap('INT') { exit 1 }
6
9
 
7
- $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
10
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
8
11
  require 'gem_updater'
9
12
 
10
13
  Bundler.ui = Bundler::UI::Shell.new
data/lib/gem_updater.rb CHANGED
@@ -1,12 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+ require 'memoist'
1
5
  require 'gem_updater/gem_file'
2
6
  require 'gem_updater/ruby_gems_fetcher'
3
7
  require 'gem_updater/source_page_parser'
4
8
 
9
+ # Base lib.
5
10
  module GemUpdater
6
-
7
11
  # Updater's main responsability is to fill changes
8
12
  # happened before and after update of `Gemfile`, and then format them.
9
13
  class Updater
14
+ extend Memoist
15
+
10
16
  attr_accessor :gemfile
11
17
 
12
18
  def initialize
@@ -43,23 +49,11 @@ module GemUpdater
43
49
 
44
50
  # For each gem, retrieve its changelog
45
51
  def fill_changelogs
46
- threads = []
47
-
48
- gemfile.changes.each do |gem_name, details|
49
- threads << Thread.new do
50
- if source_uri = find_source(gem_name, details[:source])
51
- source_page = GemUpdater::SourcePageParser.new(
52
- url: source_uri, version: details[:versions][:new]
53
- )
54
-
55
- if source_page.changelog
56
- gemfile.changes[gem_name][:changelog] = source_page.changelog
57
- end
58
- end
52
+ [].tap do |threads|
53
+ gemfile.changes.each do |gem_name, details|
54
+ threads << Thread.new { retrieve_gem_changes(gem_name, details) }
59
55
  end
60
- end
61
-
62
- threads.each(&:join)
56
+ end.each(&:join)
63
57
  end
64
58
 
65
59
  # Find where is hosted the source of a gem
@@ -76,16 +70,26 @@ module GemUpdater
76
70
  end
77
71
  end
78
72
 
73
+ def retrieve_gem_changes(gem_name, details)
74
+ source_uri = find_source(gem_name, details[:source])
75
+ return unless source_uri
76
+
77
+ source_page = GemUpdater::SourcePageParser.new(
78
+ url: source_uri, version: details[:versions][:new]
79
+ )
80
+
81
+ gemfile.changes[gem_name][:changelog] = source_page.changelog if source_page.changelog
82
+ end
83
+
79
84
  # Get the template for gem's diff.
80
85
  # It can use a custom template.
81
86
  #
82
87
  # @return [ERB] the template
83
88
  def template
84
- @template ||= begin
85
- File.read("#{Dir.home}/.gem_updater_template.erb")
86
- rescue Errno::ENOENT
87
- File.read(File.expand_path('../../lib/gem_updater_template.erb', __FILE__))
88
- end
89
+ File.read("#{Dir.home}/.gem_updater_template.erb")
90
+ rescue Errno::ENOENT
91
+ File.read(File.expand_path('../lib/gem_updater_template.erb', __dir__))
89
92
  end
93
+ memoize :template
90
94
  end
91
95
  end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler'
1
4
  require 'bundler/cli'
2
5
 
3
6
  module GemUpdater
4
-
5
7
  # GemFile is responsible for handling `Gemfile`
6
8
  class GemFile
7
9
  attr_accessor :changes
@@ -21,21 +23,20 @@ module GemUpdater
21
23
  #
22
24
  # @return [Hash] gems for which there are differences.
23
25
  def compute_changes
24
- get_spec_sets
26
+ spec_sets_diff!
25
27
 
26
28
  old_spec_set.each do |old_gem|
27
29
  updated_gem = new_spec_set.find { |new_gem| new_gem.name == old_gem.name }
30
+ next unless updated_gem && old_gem.version != updated_gem.version
28
31
 
29
- if updated_gem && old_gem.version != updated_gem.version
30
- fill_changes(old_gem, updated_gem)
31
- end
32
+ fill_changes(old_gem, updated_gem)
32
33
  end
33
34
  end
34
35
 
35
36
  private
36
37
 
37
38
  # Get the two spec sets (before and after `bundle update`)
38
- def get_spec_sets
39
+ def spec_sets_diff!
39
40
  @old_spec_set = spec_set
40
41
  reinitialize_spec_set!
41
42
  @new_spec_set = spec_set
@@ -1,12 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
  require 'nokogiri'
3
5
  require 'open-uri'
4
6
 
5
7
  module GemUpdater
6
-
7
8
  # RubyGemsFetcher is a wrapper around rubygems API.
8
9
  class RubyGemsFetcher
9
- HTTP_TOO_MANY_REQUESTS = '429'.freeze
10
+ HTTP_TOO_MANY_REQUESTS = '429'
10
11
  GEM_HOMEPAGES = %w[source_code_uri homepage_uri].freeze
11
12
 
12
13
  attr_reader :gem_name, :source
@@ -28,6 +29,14 @@ module GemUpdater
28
29
 
29
30
  private
30
31
 
32
+ # Parse JSON from a remote url.
33
+ #
34
+ # @param url [String] remote url
35
+ # @return [Hash] parsed JSON
36
+ def parse_remote_json(url)
37
+ JSON.parse(URI.parse(url).open.read)
38
+ end
39
+
31
40
  # Ask rubygems.org for source uri of gem.
32
41
  # See API: http://guides.rubygems.org/rubygems-org-api/#gem-methods
33
42
  #
@@ -35,11 +44,10 @@ module GemUpdater
35
44
  def uri_from_rubygems
36
45
  return unless source.remotes.map(&:host).include?('rubygems.org')
37
46
 
38
- if response = query_rubygems
39
- response[
40
- GEM_HOMEPAGES.find { |key| response[key] && !response[key].empty? }
41
- ]
42
- end
47
+ response = query_rubygems
48
+ return unless response
49
+
50
+ response[GEM_HOMEPAGES.find { |key| response[key] && !response[key].empty? }]
43
51
  end
44
52
 
45
53
  # Make the real query to rubygems
@@ -47,13 +55,12 @@ module GemUpdater
47
55
  #
48
56
  # @param tries [Integer|nil] (optional) how many times we tried
49
57
  def query_rubygems(tries = 0)
50
- JSON.parse(open("https://rubygems.org/api/v1/gems/#{gem_name}.json").read)
58
+ parse_remote_json("https://rubygems.org/api/v1/gems/#{gem_name}.json")
51
59
  rescue OpenURI::HTTPError => e
52
60
  # We may trigger too many requests, in which case give rubygems a break
53
61
  if e.io.status.include?(HTTP_TOO_MANY_REQUESTS)
54
- if (tries += 1) < 2
55
- sleep 1 and retry
56
- end
62
+ tries += 1
63
+ sleep 1 && retry if tries < 2
57
64
  end
58
65
  end
59
66
 
@@ -61,20 +68,16 @@ module GemUpdater
61
68
  #
62
69
  # @return [String|nil] uri of source code
63
70
  def uri_from_other_sources
64
- uri = nil
65
- source.remotes.each do |remote|
66
- break if uri
67
-
68
- uri = case remote.host
69
- when 'rubygems.org' then next # already checked
70
- when 'rails-assets.org'
71
- uri_from_railsassets
72
- else
73
- Bundler.ui.error "Source #{remote} is not supported, feel free to open a PR or an issue on https://github.com/MaximeD/gem_updater"
74
- end
71
+ source.remotes.find do |remote|
72
+ case remote.host
73
+ when 'rubygems.org' then next # already checked
74
+ when 'rails-assets.org'
75
+ return uri_from_railsassets
76
+ else
77
+ Bundler.ui.error "Source #{remote} is not supported, ' \
78
+ 'feel free to open a PR or an issue on https://github.com/MaximeD/gem_updater"
79
+ end
75
80
  end
76
-
77
- uri
78
81
  end
79
82
 
80
83
  # Ask rails-assets.org for source uri of gem.
@@ -82,22 +85,21 @@ module GemUpdater
82
85
  #
83
86
  # @return [String|nil] uri of source code
84
87
  def uri_from_railsassets
85
- if response = query_railsassets
86
- response['url'].gsub(/^git/, 'http')
87
- end
88
+ response = query_railsassets
89
+ return unless response
90
+
91
+ response['url'].gsub(/^git/, 'http')
88
92
  end
89
93
 
90
94
  # Make the real query to railsassets
95
+ # rubocop:disable Lint/SuppressedException
91
96
  def query_railsassets
92
- JSON.parse(
93
- open(
94
- "https://rails-assets.org/packages/#{gem_name.gsub(/rails-assets-/, '')}"
95
- ).read
96
- )
97
+ parse_remote_json("https://rails-assets.org/packages/#{gem_name.gsub(/rails-assets-/, '')}")
97
98
  rescue JSON::ParserError
98
99
  # if gem is not found, rails-assets returns a 200
99
100
  # with html (instead of json) containing a 500...
100
101
  rescue OpenURI::HTTPError
101
102
  end
103
+ # rubocop:enable Lint/SuppressedException
102
104
  end
103
105
  end
@@ -1,11 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'nokogiri'
2
4
  require 'open-uri'
3
5
 
4
6
  module GemUpdater
5
-
6
7
  # SourcePageParser is responsible for parsing a source page where
7
8
  # the gem code is hosted.
8
9
  class SourcePageParser
10
+ extend Memoist
11
+
9
12
  HOSTS = {
10
13
  github: /github.com/,
11
14
  bitbucket: /bitbucket.org/,
@@ -29,23 +32,16 @@ module GemUpdater
29
32
  def changelog
30
33
  return unless uri
31
34
 
32
- @changelog ||= begin
33
- Bundler.ui.warn "Looking for a changelog in #{uri}"
34
- doc = Nokogiri::HTML(open(uri))
35
-
36
- find_changelog(doc)
37
-
38
- rescue OpenURI::HTTPError # Uri points to nothing
39
- Bundler.ui.error "Cannot find #{uri}"
40
- false
41
- rescue Errno::ETIMEDOUT # timeout
42
- Bundler.ui.error "#{uri} is down"
43
- false
44
- rescue ArgumentError => e # x-oauth-basic raises userinfo not supported. [RFC3986]
45
- Bundler.ui.error e
46
- false
47
- end
35
+ Bundler.ui.warn "Looking for a changelog in #{uri}"
36
+ find_changelog(Nokogiri::HTML(uri.open))
37
+ rescue OpenURI::HTTPError # Uri points to nothing
38
+ log_error("Cannot find #{uri}")
39
+ rescue Errno::ETIMEDOUT # timeout
40
+ log_error("#{uri} is down")
41
+ rescue ArgumentError => e # x-oauth-basic raises userinfo not supported. [RFC3986]
42
+ log_error(e)
48
43
  end
44
+ memoize :changelog
49
45
 
50
46
  private
51
47
 
@@ -55,15 +51,10 @@ module GemUpdater
55
51
  # @param url [String] the url to parse
56
52
  # @return [URI] valid URI
57
53
  def correct_uri(url)
58
- return unless String === url && !url.empty?
54
+ return unless url.is_a?(String) && !url.empty?
59
55
 
60
- uri = URI(url)
61
-
62
- if uri.scheme == 'http'
63
- known_https(uri)
64
- else
65
- uri
66
- end
56
+ uri = URI.parse(url)
57
+ uri.scheme == 'http' ? known_https(uri) : uri
67
58
  end
68
59
 
69
60
  # Some uris are not https, but we know they should be,
@@ -73,13 +64,11 @@ module GemUpdater
73
64
  # @param uri [URI::HTTP]
74
65
  # @return [URI::HTTPS|URI::HTTP]
75
66
  def known_https(uri)
76
- case
77
- when uri.host =~ HOSTS[:github]
67
+ case uri.host
68
+ when HOSTS[:github]
78
69
  # remove possible subdomain like 'wiki.github.com'
79
70
  URI "https://github.com#{uri.path}"
80
- when uri.host =~ HOSTS[:bitbucket]
81
- URI "https://#{uri.host}#{uri.path}"
82
- when uri.host =~ HOSTS[:rubygems]
71
+ when HOSTS[:bitbucket], HOSTS[:rubygems]
83
72
  URI "https://#{uri.host}#{uri.path}"
84
73
  else
85
74
  uri
@@ -115,49 +104,55 @@ module GemUpdater
115
104
  MARKUP_FILES.include?(File.extname(file_name))
116
105
  end
117
106
 
107
+ def log_error(error_message)
108
+ Bundler.ui.error error_message
109
+ false
110
+ end
111
+
118
112
  # GitHubParser is responsible for parsing source code
119
113
  # hosted on github.com.
120
114
  class GitHubParser < SourcePageParser
121
- BASE_URL = 'https://github.com'.freeze
115
+ BASE_URL = 'https://github.com'
122
116
 
123
117
  attr_reader :doc, :version
124
118
 
125
119
  # @param doc [Nokogiri::XML::Element] document of source page
126
120
  # @param version [String] version of gem
121
+ # rubocop:disable Lint/MissingSuper
127
122
  def initialize(doc, version)
128
123
  @doc = doc
129
124
  @version = version
130
125
  end
126
+ # rubocop:enable Lint/MissingSuper
131
127
 
132
128
  # Finds url of changelog.
133
129
  #
134
130
  # @return [String] the URL of changelog
135
131
  def changelog
136
132
  url = find_changelog_link
133
+ return unless url
137
134
 
138
- if url
139
- full_url = BASE_URL + url
135
+ full_url = BASE_URL + url
140
136
 
141
- if changelog_may_contain_anchor?(full_url)
142
- anchor = find_anchor(full_url)
143
- full_url += anchor if anchor
144
- end
145
-
146
- full_url
137
+ if changelog_may_contain_anchor?(full_url)
138
+ anchor = find_anchor(full_url)
139
+ full_url += anchor if anchor
147
140
  end
141
+
142
+ full_url
148
143
  end
149
144
 
150
145
  private
151
146
 
152
147
  # Find which link corresponds to changelog.
148
+ # Last one is taken as files come after directories. So if there is a directory plus a file
149
+ # matching a changelog name, we ensure file is preferred.
153
150
  #
154
151
  # @return [String, nil] url of changelog
155
152
  def find_changelog_link
156
- changelog_names.find do |name|
157
- node = doc.at_css(%(table.files .content a[title^="#{name}"]))
158
-
159
- break node.attr('href') if node
160
- end
153
+ changelog_names.map do |name|
154
+ doc.at_css(%([aria-labelledby="files"] a[title^="#{name}"]))
155
+ end.compact.last&.attr('href')
161
156
  end
162
157
 
163
158
  # Looks into document to find it there is an anchor to new gem version.
@@ -165,12 +160,12 @@ module GemUpdater
165
160
  # @param url [String] url of changelog
166
161
  # @return [String, nil] anchor's href
167
162
  def find_anchor(url)
168
- changelog_page = Nokogiri::HTML(open(url))
163
+ changelog_page = Nokogiri::HTML(URI.parse(url).open)
169
164
  anchor = changelog_page.css(%(a.anchor)).find do |element|
170
165
  element.attr('href').match(version.delete('.'))
171
166
  end
172
167
 
173
- anchor.attr('href') if anchor
168
+ anchor&.attr('href')
174
169
  end
175
170
  end
176
171
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gem_updater
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 4.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maxime Demolin
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-10 00:00:00.000000000 Z
11
+ date: 2021-02-10 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
- version: '1.16'
19
+ version: '3'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - "<"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.16'
26
+ version: '3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: json
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '2.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: memoist
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.16.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.16.0
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: nokogiri
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +66,20 @@ dependencies:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
68
  version: '1.8'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.14'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.14'
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: rspec
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -66,9 +94,65 @@ dependencies:
66
94
  - - "~>"
67
95
  - !ruby/object:Gem::Version
68
96
  version: '3.7'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-performance
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 1.5.2
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 1.5.2
125
+ - !ruby/object:Gem::Dependency
126
+ name: vcr
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '6.0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '6.0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: webmock
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '3.8'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '3.8'
69
153
  description: Updates the gems of your Gemfile and fetches the links pointing to where
70
154
  their changelogs are
71
- email: akbarova.armia@gmail.com
155
+ email: maxime_dev@demol.in
72
156
  executables:
73
157
  - gem_update
74
158
  extensions: []
@@ -84,7 +168,7 @@ homepage: https://github.com/MaximeD/gem_updater
84
168
  licenses:
85
169
  - MIT
86
170
  metadata: {}
87
- post_install_message:
171
+ post_install_message:
88
172
  rdoc_options: []
89
173
  require_paths:
90
174
  - lib
@@ -92,16 +176,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
92
176
  requirements:
93
177
  - - ">="
94
178
  - !ruby/object:Gem::Version
95
- version: 2.3.0
179
+ version: 2.5.0
96
180
  required_rubygems_version: !ruby/object:Gem::Requirement
97
181
  requirements:
98
182
  - - ">="
99
183
  - !ruby/object:Gem::Version
100
184
  version: '0'
101
185
  requirements: []
102
- rubyforge_project:
103
- rubygems_version: 2.7.6
104
- signing_key:
186
+ rubygems_version: 3.2.3
187
+ signing_key:
105
188
  specification_version: 4
106
189
  summary: Update your gems and find their changelogs
107
190
  test_files: []