gem_updater 6.1.0 → 7.0.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 242ea33b7c63f871adaadd0e90a562c10d069e9409b2da9df55972de156ce734
4
- data.tar.gz: 899cdaba9b9208c33a916f7f27be10aee6347cbe28ec3509ae760bb1975ba4a7
3
+ metadata.gz: dc62f5f20f9d239d4eea066d409fd9900ba139630287a2884fe2497c09ce91e7
4
+ data.tar.gz: 0dc7e3a0446c91dc1a9d24786f381623a162f95218ce1e212d701768f2f99082
5
5
  SHA512:
6
- metadata.gz: 14d744975c7a423f7b467d86e262da0c5431cbaa0b76408a89f479280403d6c81d7d8214df5e6ab3b4251b84ca1b1d266d1228a3b6848149135681778a788230
7
- data.tar.gz: 73e57ed85b57033c36c55a30353a29c5a698444ea45edb9ad1d1db538f629217c5962b1de524a81e4ee3180ddba2c9c28103a55aa7976ba336d5da91c728cb84
6
+ metadata.gz: d7d95be3b9d660ea9af8b2b469e49613305230b6431a4c5c0ea351e49192b035546ff0da342c904062a0518fcc1c1b2327462c4380eed69b9fe189b32fa28b9f
7
+ data.tar.gz: 722d4d3924adb67a00b844c517da2b664685a23a0ebd4964e5bf3e16fc7f4fd69ce20ee79b7c905455bef6a511b1759dc4aa6382391a99fa55b15a0bc2180825
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nokogiri'
4
+ require 'open-uri'
5
+
6
+ module GemUpdater
7
+ class ChangelogParser
8
+ # ChangelogParser is responsible for parsing a changelog hosted on github.
9
+ class GithubParser
10
+ attr_reader :uri, :version
11
+
12
+ # @param uri [String] changelog uri
13
+ # @param version [String] version of gem
14
+ def initialize(uri:, version:)
15
+ @uri = uri
16
+ @version = version
17
+ end
18
+
19
+ # Finds anchor in changelog, otherwise return the base uri.
20
+ #
21
+ # @return [String] the URL of changelog
22
+ def changelog
23
+ uri + find_anchor(document).to_s
24
+ end
25
+
26
+ private
27
+
28
+ # Opens changelog url and parses it.
29
+ #
30
+ # @return [Nokogiri::HTML4::Document] the changelog
31
+ def document
32
+ Nokogiri::HTML(URI.parse(uri).open, nil, Encoding::UTF_8.to_s)
33
+ end
34
+
35
+ # Looks into document to find it there is an anchor to new gem version.
36
+ #
37
+ # @param doc [Nokogiri::HTML4::Document] document
38
+ # @return [String, nil] anchor's href
39
+ def find_anchor(doc)
40
+ anchor = doc.xpath('//a[contains(@class, "heading-link")]').find do |element|
41
+ element.attr('href').match(version.delete('.'))
42
+ end
43
+ return unless anchor
44
+
45
+ anchor.attr('href').gsub(%(\\"), '')
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nokogiri'
4
+ require 'open-uri'
5
+ require 'gem_updater/changelog_parser/github_parser'
6
+
7
+ module GemUpdater
8
+ # ChangelogParser is responsible for parsing a source page where
9
+ # the gem code is hosted.
10
+ class ChangelogParser
11
+ MARKUP_FILES = %w[.md .rdoc .textile].freeze
12
+
13
+ attr_reader :uri, :version
14
+
15
+ # @param uri [String] uri of changelog
16
+ # @param version [String] version of gem
17
+ def initialize(uri:, version:)
18
+ @uri = uri
19
+ @version = version
20
+ end
21
+
22
+ # Get the changelog in an uri.
23
+ #
24
+ # @return [String, nil] URL of changelog
25
+ def changelog
26
+ return uri unless changelog_may_contain_anchor?
27
+
28
+ parse_changelog
29
+ rescue OpenURI::HTTPError # Uri points to nothing
30
+ log_error_and_return_uri("Cannot find #{uri}")
31
+ rescue Errno::ETIMEDOUT # timeout
32
+ log_error_and_return_uri("#{URI.parse(uri).host} is down")
33
+ rescue ArgumentError => e # x-oauth-basic raises userinfo not supported. [RFC3986]
34
+ log_error_and_return_uri(e)
35
+ end
36
+
37
+ private
38
+
39
+ # Try to find where changelog might be.
40
+ #
41
+ # @param doc [Nokogiri::XML::Element] document of source page
42
+ def parse_changelog
43
+ case URI.parse(uri).host
44
+ when 'github.com'
45
+ GithubParser.new(uri: uri, version: version).changelog
46
+ else
47
+ uri
48
+ end
49
+ end
50
+
51
+ # Some documents like the one written in markdown may contain
52
+ # a direct anchor to specific version.
53
+ #
54
+ # @return [Boolean] true if file may contain an anchor
55
+ def changelog_may_contain_anchor?
56
+ MARKUP_FILES.include?(File.extname(uri.to_s))
57
+ end
58
+
59
+ def log_error_and_return_uri(error_message)
60
+ Bundler.ui.error error_message
61
+ uri
62
+ end
63
+ end
64
+ end
@@ -4,8 +4,8 @@ require 'bundler'
4
4
  require 'bundler/cli'
5
5
 
6
6
  module GemUpdater
7
- # GemFile is responsible for handling `Gemfile`
8
- class GemFile
7
+ # Gemfile is responsible for handling `Gemfile`
8
+ class Gemfile
9
9
  attr_accessor :changes
10
10
  attr_reader :old_spec_set, :new_spec_set
11
11
 
@@ -8,23 +8,22 @@ module GemUpdater
8
8
  # RubyGemsFetcher is a wrapper around rubygems API.
9
9
  class RubyGemsFetcher
10
10
  HTTP_TOO_MANY_REQUESTS = '429'
11
- GEM_HOMEPAGES = %w[source_code_uri homepage_uri].freeze
12
11
 
13
- attr_reader :gem_name, :source
12
+ attr_reader :gem_name
14
13
 
15
14
  # @param gem_name [String] name of the gem
16
- # @param source [Bundler::Source] source of gem
17
- def initialize(gem_name, source)
15
+ def initialize(gem_name)
18
16
  @gem_name = gem_name
19
- @source = source
20
17
  end
21
18
 
22
- # Finds where code is hosted.
23
- # Most likely in will be on rubygems, else look in other sources.
19
+ # Finds the changelog uri.
20
+ # It asks rubygems.org for changelog_uri of gem.
21
+ # See API: http://guides.rubygems.org/rubygems-org-api/#gem-methods
24
22
  #
25
- # @return [String|nil] url of gem source code
26
- def source_uri
27
- uri_from_rubygems || uri_from_other_sources
23
+ # @return [String|nil] uri of changelog
24
+ def changelog_uri
25
+ response = query_rubygems
26
+ response.to_h['changelog_uri']
28
27
  end
29
28
 
30
29
  private
@@ -37,19 +36,6 @@ module GemUpdater
37
36
  JSON.parse(URI.parse(url).open.read)
38
37
  end
39
38
 
40
- # Ask rubygems.org for source uri of gem.
41
- # See API: http://guides.rubygems.org/rubygems-org-api/#gem-methods
42
- #
43
- # @return [String|nil] uri of source code
44
- def uri_from_rubygems
45
- return unless source.remotes.map(&:host).include?('rubygems.org')
46
-
47
- response = query_rubygems
48
- return unless response
49
-
50
- response[GEM_HOMEPAGES.find { |key| response[key] && !response[key].empty? }]
51
- end
52
-
53
39
  # Make the real query to rubygems
54
40
  # It may fail in case we trigger too many requests
55
41
  #
@@ -63,43 +49,5 @@ module GemUpdater
63
49
  sleep 1 && retry if tries < 2
64
50
  end
65
51
  end
66
-
67
- # Look if gem can be found in another remote
68
- #
69
- # @return [String|nil] uri of source code
70
- def uri_from_other_sources
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
80
- end
81
- end
82
-
83
- # Ask rails-assets.org for source uri of gem.
84
- # API is at : https://rails-assets.org/packages/package_name
85
- #
86
- # @return [String|nil] uri of source code
87
- def uri_from_railsassets
88
- response = query_railsassets
89
- return unless response
90
-
91
- response['url'].gsub(/^git/, 'http')
92
- end
93
-
94
- # Make the real query to railsassets
95
- # rubocop:disable Lint/SuppressedException
96
- def query_railsassets
97
- parse_remote_json("https://rails-assets.org/packages/#{gem_name.gsub('rails-assets-', '')}")
98
- rescue JSON::ParserError
99
- # if gem is not found, rails-assets returns a 200
100
- # with html (instead of json) containing a 500...
101
- rescue OpenURI::HTTPError
102
- end
103
- # rubocop:enable Lint/SuppressedException
104
52
  end
105
53
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GemUpdater
4
- VERSION = '6.1.0'
4
+ VERSION = '7.0.0'
5
5
  end
data/lib/gem_updater.rb CHANGED
@@ -1,22 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'erb'
4
- require 'memoist'
5
- require 'gem_updater/gem_file'
4
+ require 'gem_updater/changelog_parser'
5
+ require 'gem_updater/gemfile'
6
6
  require 'gem_updater/ruby_gems_fetcher'
7
- require 'gem_updater/source_page_parser'
8
7
 
9
8
  # Base lib.
10
9
  module GemUpdater
11
10
  # Updater's main responsability is to fill changes
12
11
  # happened before and after update of `Gemfile`, and then format them.
13
12
  class Updater
14
- extend Memoist
13
+ RUBYGEMS_SOURCE_NAME = 'rubygems repository https://rubygems.org/'
15
14
 
16
15
  attr_accessor :gemfile
17
16
 
18
17
  def initialize
19
- @gemfile = GemUpdater::GemFile.new
18
+ @gemfile = GemUpdater::Gemfile.new
20
19
  end
21
20
 
22
21
  # Update process.
@@ -52,35 +51,23 @@ module GemUpdater
52
51
  # For each gem, retrieve its changelog
53
52
  def fill_changelogs
54
53
  [].tap do |threads|
55
- gemfile.changes.each do |gem_name, details|
56
- threads << Thread.new { retrieve_gem_changes(gem_name, details) }
54
+ gemfile.changes.each do |gem_changes, details|
55
+ threads << Thread.new { retrieve_changelog(gem_changes, details) }
57
56
  end
58
57
  end.each(&:join)
59
58
  end
60
59
 
61
- # Find where is hosted the source of a gem
62
- #
63
- # @param gem [String] the name of the gem
64
- # @param source [Bundler::Source] gem's source
65
- # @return [String] url where gem is hosted
66
- def find_source(gem, source)
67
- case source
68
- when Bundler::Source::Rubygems
69
- GemUpdater::RubyGemsFetcher.new(gem, source).source_uri
70
- when Bundler::Source::Git
71
- source.uri.gsub(/^git/, 'http').chomp('.git')
72
- end
73
- end
60
+ # Get the changelog URL.
61
+ def retrieve_changelog(gem_name, details)
62
+ return unless details[:source].name == RUBYGEMS_SOURCE_NAME
74
63
 
75
- def retrieve_gem_changes(gem_name, details)
76
- source_uri = find_source(gem_name, details[:source])
77
- return unless source_uri
64
+ changelog_uri = RubyGemsFetcher.new(gem_name).changelog_uri
78
65
 
79
- source_page = GemUpdater::SourcePageParser.new(
80
- url: source_uri, version: details[:versions][:new]
81
- )
66
+ return unless changelog_uri
82
67
 
83
- gemfile.changes[gem_name][:changelog] = source_page.changelog if source_page.changelog
68
+ changelog = ChangelogParser
69
+ .new(uri: changelog_uri, version: details.dig(:versions, :new)).changelog
70
+ gemfile.changes[gem_name][:changelog] = changelog&.to_s
84
71
  end
85
72
 
86
73
  # Get the template for gem's diff.
@@ -92,6 +79,5 @@ module GemUpdater
92
79
  rescue Errno::ENOENT
93
80
  File.read(File.expand_path('../lib/gem_updater_template.erb', __dir__))
94
81
  end
95
- memoize :template
96
82
  end
97
83
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gem_updater
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.1.0
4
+ version: 7.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maxime Demolin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-15 00:00:00.000000000 Z
11
+ date: 2023-09-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -38,20 +38,6 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '2.6'
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.2
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.2
55
41
  - !ruby/object:Gem::Dependency
56
42
  name: nokogiri
57
43
  requirement: !ruby/object:Gem::Requirement
@@ -160,9 +146,10 @@ extra_rdoc_files: []
160
146
  files:
161
147
  - bin/gem_update
162
148
  - lib/gem_updater.rb
163
- - lib/gem_updater/gem_file.rb
149
+ - lib/gem_updater/changelog_parser.rb
150
+ - lib/gem_updater/changelog_parser/github_parser.rb
151
+ - lib/gem_updater/gemfile.rb
164
152
  - lib/gem_updater/ruby_gems_fetcher.rb
165
- - lib/gem_updater/source_page_parser.rb
166
153
  - lib/gem_updater/version.rb
167
154
  - lib/gem_updater_template.erb
168
155
  homepage: https://github.com/MaximeD/gem_updater
@@ -1,173 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'nokogiri'
4
- require 'open-uri'
5
-
6
- module GemUpdater
7
- # SourcePageParser is responsible for parsing a source page where
8
- # the gem code is hosted.
9
- class SourcePageParser
10
- extend Memoist
11
-
12
- HOSTS = {
13
- github: /github.com/,
14
- bitbucket: /bitbucket.org/,
15
- rubygems: /rubygems.org/
16
- }.freeze
17
- MARKUP_FILES = %w[.md .rdoc .textile].freeze
18
- CHANGELOG_NAMES = %w[changelog ChangeLog history changes news].freeze
19
-
20
- attr_reader :uri, :version
21
-
22
- # @param url [String] url of page
23
- # @param version [String] version of gem
24
- def initialize(url: nil, version: nil)
25
- @uri = correct_uri(url)
26
- @version = version
27
- end
28
-
29
- # Get the changelog in an uri.
30
- #
31
- # @return [String, nil] URL of changelog
32
- def changelog
33
- return unless uri
34
-
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)
43
- end
44
- memoize :changelog
45
-
46
- private
47
-
48
- # Some gems have 'http://github.com' as URI which will redirect to https
49
- # leading `open_uri` to crash.
50
- #
51
- # @param url [String] the url to parse
52
- # @return [URI] valid URI
53
- def correct_uri(url)
54
- return unless url.is_a?(String) && !url.empty?
55
-
56
- uri = URI.parse(url)
57
- uri.scheme == 'http' ? known_https(uri) : uri
58
- end
59
-
60
- # Some uris are not https, but we know they should be,
61
- # in which case we have an https redirection
62
- # which is not properly handled by open-uri
63
- #
64
- # @param uri [URI::HTTP]
65
- # @return [URI::HTTPS|URI::HTTP]
66
- def known_https(uri)
67
- case uri.host
68
- when HOSTS[:github]
69
- # remove possible subdomain like 'wiki.github.com'
70
- URI "https://github.com#{uri.path}"
71
- when HOSTS[:bitbucket], HOSTS[:rubygems]
72
- URI "https://#{uri.host}#{uri.path}"
73
- else
74
- uri
75
- end
76
- end
77
-
78
- # Try to find where changelog might be.
79
- #
80
- # @param doc [Nokogiri::XML::Element] document of source page
81
- def find_changelog(doc)
82
- case uri.host
83
- when 'github.com'
84
- GitHubParser.new(doc, version).changelog
85
- end
86
- end
87
-
88
- # List possible names for a changelog
89
- # since humans may have many many ways to call it.
90
- #
91
- # @return [Array] list of possible names
92
- def changelog_names
93
- CHANGELOG_NAMES.flat_map do |name|
94
- [name, name.upcase, name.capitalize]
95
- end.uniq
96
- end
97
-
98
- # Some documents like the one written in markdown may contain
99
- # a direct anchor to specific version.
100
- #
101
- # @param file_name [String] file name of changelog
102
- # @return [Boolean] true if file may contain an anchor
103
- def changelog_may_contain_anchor?(file_name)
104
- MARKUP_FILES.include?(File.extname(file_name))
105
- end
106
-
107
- def log_error(error_message)
108
- Bundler.ui.error error_message
109
- false
110
- end
111
-
112
- # GitHubParser is responsible for parsing source code
113
- # hosted on github.com.
114
- class GitHubParser < SourcePageParser
115
- BASE_URL = 'https://github.com'
116
-
117
- attr_reader :doc, :version
118
-
119
- # @param doc [Nokogiri::XML::Element] document of source page
120
- # @param version [String] version of gem
121
- # rubocop:disable Lint/MissingSuper
122
- def initialize(doc, version)
123
- @doc = doc
124
- @version = version
125
- end
126
- # rubocop:enable Lint/MissingSuper
127
-
128
- # Finds url of changelog.
129
- #
130
- # @return [String] the URL of changelog
131
- def changelog
132
- url = find_changelog_link
133
- return unless url
134
-
135
- full_url = BASE_URL + url
136
-
137
- if changelog_may_contain_anchor?(full_url)
138
- anchor = find_anchor(full_url)
139
- full_url += anchor if anchor
140
- end
141
-
142
- full_url
143
- end
144
-
145
- private
146
-
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.
150
- #
151
- # @return [String, nil] url of changelog
152
- def find_changelog_link
153
- changelog_names.filter_map do |name|
154
- doc.at_css(%([aria-labelledby="files"] a[title^="#{name}"]))
155
- end.last&.attr('href')
156
- end
157
-
158
- # Looks into document to find it there is an anchor to new gem version.
159
- #
160
- # @param url [String] url of changelog
161
- # @return [String, nil] anchor's href
162
- def find_anchor(url)
163
- changelog_page = Nokogiri::HTML(URI.parse(url).open)
164
- anchor = changelog_page.xpath('//a[contains(@class, "anchor")]').find do |element|
165
- element.attr('href').match(version.delete('.'))
166
- end
167
- return unless anchor
168
-
169
- anchor.attr('href').gsub(%(\\"), '')
170
- end
171
- end
172
- end
173
- end