gem_updater 0.4.5 → 0.5.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 +4 -4
- data/bin/gem_update +12 -8
- data/lib/gem_updater.rb +18 -15
- data/lib/gem_updater/gem_file.rb +21 -8
- data/lib/gem_updater/ruby_gems_fetcher.rb +36 -27
- data/lib/gem_updater/source_page_parser.rb +62 -39
- data/lib/gem_updater_template.erb +3 -3
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 66ba0cea1b0c09848cc7e9662257d9f9e366b487
|
4
|
+
data.tar.gz: 20527e80b3fc8da321ef1539a59d4e693aac2885
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0740383ec2b47aa3d68adeb7f96e49e7bb7210e8c08ae74d02efc9557e3fc0a17e18fa7de9be0bfae404db30a091f0ba5dc09c7fe53d8c1b4c69dd8fb8beb559
|
7
|
+
data.tar.gz: bb02299d459070caadb163aa9fe2f9624eacd5bd86aca23510844cff14ea5a19d50c52bdce416104194e45d13f51eed3c591808f5b82c95604d800bc6490440d
|
data/bin/gem_update
CHANGED
@@ -2,34 +2,38 @@
|
|
2
2
|
require 'optparse'
|
3
3
|
|
4
4
|
# Exit cleanly from an early interrupt
|
5
|
-
Signal.trap(
|
5
|
+
Signal.trap('INT') { exit 1 }
|
6
6
|
|
7
|
-
|
7
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
8
8
|
require 'gem_updater'
|
9
9
|
|
10
10
|
Bundler.ui = Bundler::UI::Shell.new
|
11
11
|
|
12
12
|
options = {}
|
13
13
|
OptionParser.new do |opts|
|
14
|
-
opts.on(
|
15
|
-
options[
|
14
|
+
opts.on('-c', '--commit', 'Auto commit') do |v|
|
15
|
+
options[:commit] = v
|
16
16
|
end
|
17
17
|
end.parse!
|
18
18
|
|
19
19
|
gems = GemUpdater::Updater.new
|
20
|
-
gems.update!(
|
20
|
+
gems.update!(ARGV)
|
21
21
|
|
22
22
|
if gems.gemfile.changes.any?
|
23
|
-
if options[
|
23
|
+
if options[:commit]
|
24
24
|
require 'tempfile'
|
25
|
-
file = Tempfile.new(
|
25
|
+
file = Tempfile.new('gem_updater')
|
26
26
|
file.write "UPDATE gems\n\n"
|
27
27
|
file.write gems.format_diff.join
|
28
28
|
file.close
|
29
29
|
|
30
30
|
gemfile = Bundler.default_gemfile.to_s
|
31
31
|
|
32
|
-
system %(
|
32
|
+
system %(
|
33
|
+
git add #{gemfile} #{gemfile}.lock \
|
34
|
+
&& git commit -t #{file.path} --allow-empty-message
|
35
|
+
)
|
36
|
+
|
33
37
|
file.unlink
|
34
38
|
else
|
35
39
|
Bundler.ui.confirm "\nHere are your changes:"
|
data/lib/gem_updater.rb
CHANGED
@@ -4,8 +4,8 @@ require 'gem_updater/source_page_parser'
|
|
4
4
|
|
5
5
|
module GemUpdater
|
6
6
|
|
7
|
-
# Updater's main responsability is to fill changes
|
8
|
-
# of `Gemfile`, and then format them.
|
7
|
+
# Updater's main responsability is to fill changes
|
8
|
+
# happened before and after update of `Gemfile`, and then format them.
|
9
9
|
class Updater
|
10
10
|
attr_accessor :gemfile
|
11
11
|
|
@@ -19,8 +19,8 @@ module GemUpdater
|
|
19
19
|
# 2. find changelogs for updated gems
|
20
20
|
#
|
21
21
|
# @param gems [Array] list of gems to update
|
22
|
-
def update!(
|
23
|
-
gemfile.update!(
|
22
|
+
def update!(gems)
|
23
|
+
gemfile.update!(gems)
|
24
24
|
gemfile.compute_changes
|
25
25
|
|
26
26
|
fill_changelogs
|
@@ -35,7 +35,7 @@ module GemUpdater
|
|
35
35
|
# on the gems that were updated.
|
36
36
|
def format_diff
|
37
37
|
gemfile.changes.map do |gem, details|
|
38
|
-
ERB.new(
|
38
|
+
ERB.new(template, nil, '<>').result(binding)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
@@ -47,29 +47,32 @@ module GemUpdater
|
|
47
47
|
|
48
48
|
gemfile.changes.each do |gem_name, details|
|
49
49
|
threads << Thread.new do
|
50
|
-
if source_uri = find_source(
|
51
|
-
source_page = GemUpdater::SourcePageParser.new(
|
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
|
+
)
|
52
54
|
|
53
|
-
|
55
|
+
if source_page.changelog
|
56
|
+
gemfile.changes[gem_name][:changelog] = source_page.changelog
|
57
|
+
end
|
54
58
|
end
|
55
59
|
end
|
56
60
|
end
|
57
61
|
|
58
|
-
threads.each(
|
62
|
+
threads.each(&:join)
|
59
63
|
end
|
60
64
|
|
61
|
-
|
62
65
|
# Find where is hosted the source of a gem
|
63
66
|
#
|
64
67
|
# @param gem [String] the name of the gem
|
65
68
|
# @param source [Bundler::Source] gem's source
|
66
69
|
# @return [String] url where gem is hosted
|
67
|
-
def find_source(
|
70
|
+
def find_source(gem, source)
|
68
71
|
case source
|
69
72
|
when Bundler::Source::Rubygems
|
70
|
-
GemUpdater::RubyGemsFetcher.new(
|
73
|
+
GemUpdater::RubyGemsFetcher.new(gem, source).source_uri
|
71
74
|
when Bundler::Source::Git
|
72
|
-
source.uri.gsub(
|
75
|
+
source.uri.gsub(/^git/, 'http').chomp('.git')
|
73
76
|
end
|
74
77
|
end
|
75
78
|
|
@@ -79,9 +82,9 @@ module GemUpdater
|
|
79
82
|
# @return [ERB] the template
|
80
83
|
def template
|
81
84
|
@template ||= begin
|
82
|
-
File.read(
|
85
|
+
File.read("#{Dir.home}/.gem_updater_template.erb")
|
83
86
|
rescue Errno::ENOENT
|
84
|
-
File.read(
|
87
|
+
File.read(File.expand_path('../../lib/gem_updater_template.erb', __FILE__))
|
85
88
|
end
|
86
89
|
end
|
87
90
|
end
|
data/lib/gem_updater/gem_file.rb
CHANGED
@@ -12,9 +12,9 @@ module GemUpdater
|
|
12
12
|
end
|
13
13
|
|
14
14
|
# Run `bundle update` to update gems.
|
15
|
-
def update!(
|
16
|
-
Bundler.ui.warn
|
17
|
-
Bundler::CLI.start(
|
15
|
+
def update!(gems)
|
16
|
+
Bundler.ui.warn 'Updating gems...'
|
17
|
+
Bundler::CLI.start(['update'] + gems)
|
18
18
|
end
|
19
19
|
|
20
20
|
# Compute the diffs between two `Gemfile.lock`.
|
@@ -24,10 +24,10 @@ module GemUpdater
|
|
24
24
|
get_spec_sets
|
25
25
|
|
26
26
|
old_spec_set.each do |old_gem|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
updated_gem = new_spec_set.find { |new_gem| new_gem.name == old_gem.name }
|
28
|
+
|
29
|
+
if updated_gem && old_gem.version != updated_gem.version
|
30
|
+
fill_changes(old_gem, updated_gem)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
@@ -52,7 +52,20 @@ module GemUpdater
|
|
52
52
|
# will return the same result.
|
53
53
|
# Use a hacky way to tell bundle we want to parse the new `Gemfile.lock`
|
54
54
|
def reinitialize_spec_set!
|
55
|
-
Bundler.remove_instance_variable(
|
55
|
+
Bundler.remove_instance_variable('@locked_gems')
|
56
|
+
end
|
57
|
+
|
58
|
+
# Add changes to between two versions of a gem
|
59
|
+
#
|
60
|
+
# @param old_gem [Bundler::LazySpecification]
|
61
|
+
# @param new_gem [Bundler::LazySpecification]
|
62
|
+
def fill_changes(old_gem, updated_gem)
|
63
|
+
changes[old_gem.name] = {
|
64
|
+
versions: {
|
65
|
+
old: old_gem.version.to_s, new: updated_gem.version.to_s
|
66
|
+
},
|
67
|
+
source: updated_gem.source
|
68
|
+
}
|
56
69
|
end
|
57
70
|
end
|
58
71
|
end
|
@@ -6,11 +6,14 @@ module GemUpdater
|
|
6
6
|
|
7
7
|
# RubyGemsFetcher is a wrapper around rubygems API.
|
8
8
|
class RubyGemsFetcher
|
9
|
+
HTTP_TOO_MANY_REQUESTS = '429'.freeze
|
10
|
+
GEM_HOMEPAGES = %w[source_code_uri homepage_uri].freeze
|
11
|
+
|
9
12
|
attr_reader :gem_name, :source
|
10
13
|
|
11
14
|
# @param gem_name [String] name of the gem
|
12
15
|
# @param source [Bundler::Source] source of gem
|
13
|
-
def initialize(
|
16
|
+
def initialize(gem_name, source)
|
14
17
|
@gem_name = gem_name
|
15
18
|
@source = source
|
16
19
|
end
|
@@ -30,29 +33,30 @@ module GemUpdater
|
|
30
33
|
#
|
31
34
|
# @return [String|nil] uri of source code
|
32
35
|
def uri_from_rubygems
|
33
|
-
return unless source.remotes.map(
|
34
|
-
tries = 0
|
35
|
-
|
36
|
-
response = begin
|
37
|
-
JSON.parse( open( "https://rubygems.org/api/v1/gems/#{gem_name}.json" ).read )
|
38
|
-
rescue OpenURI::HTTPError => e
|
39
|
-
# We may trigger too many requests, in which case give rubygems a break
|
40
|
-
if e.io.status.include?( '429' )
|
41
|
-
if ( tries += 1 ) < 2
|
42
|
-
sleep 1 and retry
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
36
|
+
return unless source.remotes.map(&:host).include?('rubygems.org')
|
46
37
|
|
47
|
-
if response
|
38
|
+
if response = query_rubygems
|
48
39
|
response[
|
49
|
-
|
50
|
-
response[ key ] && !response[ key ].empty?
|
51
|
-
end
|
40
|
+
GEM_HOMEPAGES.find { |key| response[key] && !response[key].empty? }
|
52
41
|
]
|
53
42
|
end
|
54
43
|
end
|
55
44
|
|
45
|
+
# Make the real query to rubygems
|
46
|
+
# It may fail in case we trigger too many requests
|
47
|
+
#
|
48
|
+
# @param tries [Integer|nil] (optional) how many times we tried
|
49
|
+
def query_rubygems(tries = 0)
|
50
|
+
JSON.parse(open("https://rubygems.org/api/v1/gems/#{gem_name}.json").read)
|
51
|
+
rescue OpenURI::HTTPError => e
|
52
|
+
# We may trigger too many requests, in which case give rubygems a break
|
53
|
+
if e.io.status.include?(HTTP_TOO_MANY_REQUESTS)
|
54
|
+
if (tries += 1) < 2
|
55
|
+
sleep 1 and retry
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
56
60
|
# Look if gem can be found in another remote
|
57
61
|
#
|
58
62
|
# @return [String|nil] uri of source code
|
@@ -78,17 +82,22 @@ module GemUpdater
|
|
78
82
|
#
|
79
83
|
# @return [String|nil] uri of source code
|
80
84
|
def uri_from_railsassets
|
81
|
-
|
82
|
-
response
|
83
|
-
rescue JSON::ParserError
|
84
|
-
# if gem is not found, rails-assets returns a 200
|
85
|
-
# with html (instead of json) containing a 500...
|
86
|
-
rescue OpenURI::HTTPError
|
85
|
+
if response = query_railsassets
|
86
|
+
response['url'].gsub(/^git/, 'http')
|
87
87
|
end
|
88
|
+
end
|
88
89
|
|
89
|
-
|
90
|
-
|
91
|
-
|
90
|
+
# Make the real query to railsassets
|
91
|
+
def query_railsassets
|
92
|
+
JSON.parse(
|
93
|
+
open(
|
94
|
+
"https://rails-assets.org/packages/#{gem_name.gsub(/rails-assets-/, '')}"
|
95
|
+
).read
|
96
|
+
)
|
97
|
+
rescue JSON::ParserError
|
98
|
+
# if gem is not found, rails-assets returns a 200
|
99
|
+
# with html (instead of json) containing a 500...
|
100
|
+
rescue OpenURI::HTTPError
|
92
101
|
end
|
93
102
|
end
|
94
103
|
end
|
@@ -6,11 +6,16 @@ module GemUpdater
|
|
6
6
|
# SourcePageParser is responsible for parsing a source page where
|
7
7
|
# the gem code is hosted.
|
8
8
|
class SourcePageParser
|
9
|
+
HOSTS = { github: /github.com/, bitbucket: /bitbucket.org/ }.freeze
|
10
|
+
MARKUP_FILES = %w[.md .rdoc .textile].freeze
|
11
|
+
CHANGELOG_NAMES = %w[changelog ChangeLog history changes news].freeze
|
12
|
+
|
13
|
+
attr_reader :uri, :version
|
9
14
|
|
10
15
|
# @param url [String] url of page
|
11
16
|
# @param version [String] version of gem
|
12
|
-
def initialize(
|
13
|
-
@uri = correct_uri(
|
17
|
+
def initialize(url: nil, version: nil)
|
18
|
+
@uri = correct_uri(url)
|
14
19
|
@version = version
|
15
20
|
end
|
16
21
|
|
@@ -19,16 +24,18 @@ module GemUpdater
|
|
19
24
|
# @return [String, nil] URL of changelog
|
20
25
|
def changelog
|
21
26
|
@changelog ||= begin
|
22
|
-
|
23
|
-
|
27
|
+
if uri
|
28
|
+
Bundler.ui.warn "Looking for a changelog in #{uri}"
|
29
|
+
doc = Nokogiri::HTML(open(uri))
|
24
30
|
|
25
|
-
|
31
|
+
find_changelog(doc)
|
32
|
+
end
|
26
33
|
|
27
34
|
rescue OpenURI::HTTPError # Uri points to nothing
|
28
|
-
Bundler.ui.error "Cannot find #{
|
35
|
+
Bundler.ui.error "Cannot find #{uri}"
|
29
36
|
false
|
30
37
|
rescue Errno::ETIMEDOUT # timeout
|
31
|
-
Bundler.ui.error "#{
|
38
|
+
Bundler.ui.error "#{uri} is down"
|
32
39
|
false
|
33
40
|
end
|
34
41
|
end
|
@@ -40,28 +47,43 @@ module GemUpdater
|
|
40
47
|
#
|
41
48
|
# @param url [String] the url to parse
|
42
49
|
# @return [URI] valid URI
|
43
|
-
def correct_uri(
|
44
|
-
|
50
|
+
def correct_uri(url)
|
51
|
+
return unless String === url && !url.empty?
|
52
|
+
|
53
|
+
uri = URI(url)
|
54
|
+
|
45
55
|
if uri.scheme == 'http'
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
uri = URI "https://github.com#{uri.path}"
|
50
|
-
when uri.host.match( 'bitbucket.org' )
|
51
|
-
uri = URI "https://#{uri.host}#{uri.path}"
|
52
|
-
end
|
56
|
+
known_https(uri)
|
57
|
+
else
|
58
|
+
uri
|
53
59
|
end
|
60
|
+
end
|
54
61
|
|
55
|
-
|
62
|
+
# Some uris are not https, but we know they should be,
|
63
|
+
# in which case we have an https redirection
|
64
|
+
# which is not properly handled by open-uri
|
65
|
+
#
|
66
|
+
# @param uri [URI::HTTP]
|
67
|
+
# @return [URI::HTTPS|URI::HTTP]
|
68
|
+
def known_https(uri)
|
69
|
+
case
|
70
|
+
when uri.host =~ HOSTS[:github]
|
71
|
+
# remove possible subdomain like 'wiki.github.com'
|
72
|
+
URI "https://github.com#{uri.path}"
|
73
|
+
when uri.host =~ HOSTS[:bitbucket]
|
74
|
+
URI "https://#{uri.host}#{uri.path}"
|
75
|
+
else
|
76
|
+
uri
|
77
|
+
end
|
56
78
|
end
|
57
79
|
|
58
80
|
# Try to find where changelog might be.
|
59
81
|
#
|
60
82
|
# @param doc [Nokogiri::XML::Element] document of source page
|
61
|
-
def find_changelog(
|
62
|
-
case
|
83
|
+
def find_changelog(doc)
|
84
|
+
case uri.host
|
63
85
|
when 'github.com'
|
64
|
-
GitHubParser.new(
|
86
|
+
GitHubParser.new(doc, version).changelog
|
65
87
|
end
|
66
88
|
end
|
67
89
|
|
@@ -70,9 +92,9 @@ module GemUpdater
|
|
70
92
|
#
|
71
93
|
# @return [Array] list of possible names
|
72
94
|
def changelog_names
|
73
|
-
|
74
|
-
|
75
|
-
|
95
|
+
CHANGELOG_NAMES.flat_map do |name|
|
96
|
+
[name, name.upcase, name.capitalize]
|
97
|
+
end.uniq
|
76
98
|
end
|
77
99
|
|
78
100
|
# Some documents like the one written in markdown may contain
|
@@ -80,18 +102,20 @@ module GemUpdater
|
|
80
102
|
#
|
81
103
|
# @param file_name [String] file name of changelog
|
82
104
|
# @return [Boolean] true if file may contain an anchor
|
83
|
-
def changelog_may_contain_anchor?(
|
84
|
-
|
105
|
+
def changelog_may_contain_anchor?(file_name)
|
106
|
+
MARKUP_FILES.include?(File.extname(file_name))
|
85
107
|
end
|
86
108
|
|
87
109
|
# GitHubParser is responsible for parsing source code
|
88
110
|
# hosted on github.com.
|
89
111
|
class GitHubParser < SourcePageParser
|
90
|
-
BASE_URL = 'https://github.com'
|
112
|
+
BASE_URL = 'https://github.com'.freeze
|
113
|
+
|
114
|
+
attr_reader :doc, :version
|
91
115
|
|
92
116
|
# @param doc [Nokogiri::XML::Element] document of source page
|
93
117
|
# @param version [String] version of gem
|
94
|
-
def initialize(
|
118
|
+
def initialize(doc, version)
|
95
119
|
@doc = doc
|
96
120
|
@version = version
|
97
121
|
end
|
@@ -105,8 +129,8 @@ module GemUpdater
|
|
105
129
|
if url
|
106
130
|
full_url = BASE_URL + url
|
107
131
|
|
108
|
-
if changelog_may_contain_anchor?(
|
109
|
-
anchor = find_anchor(
|
132
|
+
if changelog_may_contain_anchor?(full_url)
|
133
|
+
anchor = find_anchor(full_url)
|
110
134
|
full_url += anchor if anchor
|
111
135
|
end
|
112
136
|
|
@@ -121,10 +145,9 @@ module GemUpdater
|
|
121
145
|
# @return [String, nil] url of changelog
|
122
146
|
def find_changelog_link
|
123
147
|
changelog_names.find do |name|
|
124
|
-
node =
|
125
|
-
|
126
|
-
|
127
|
-
end
|
148
|
+
node = doc.at_css(%(table.files a[title^="#{name}"]))
|
149
|
+
|
150
|
+
break node.attr('href') if node
|
128
151
|
end
|
129
152
|
end
|
130
153
|
|
@@ -132,13 +155,13 @@ module GemUpdater
|
|
132
155
|
#
|
133
156
|
# @param url [String] url of changelog
|
134
157
|
# @return [String, nil] anchor's href
|
135
|
-
def find_anchor(
|
136
|
-
changelog_page = Nokogiri::HTML(
|
137
|
-
anchor = changelog_page.css(
|
138
|
-
|
139
|
-
if anchor
|
140
|
-
anchor.attr( 'href' )
|
158
|
+
def find_anchor(url)
|
159
|
+
changelog_page = Nokogiri::HTML(open(url))
|
160
|
+
anchor = changelog_page.css(%(a.anchor)).find do |element|
|
161
|
+
element.attr('href').match(version.delete('.'))
|
141
162
|
end
|
163
|
+
|
164
|
+
anchor.attr('href') if anchor
|
142
165
|
end
|
143
166
|
end
|
144
167
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
* <%= gem %> <%= details[
|
2
|
-
<% if details[
|
3
|
-
[changelog](<%= details[
|
1
|
+
* <%= gem %> <%= details[:versions][:old] %> → <%= details[:versions][:new] %>
|
2
|
+
<% if details[:changelog] %>
|
3
|
+
[changelog](<%= details[:changelog] %>)
|
4
4
|
<% end %>
|
5
5
|
|
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: 0.
|
4
|
+
version: 0.5.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: 2016-
|
11
|
+
date: 2016-04-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -100,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
100
|
version: '0'
|
101
101
|
requirements: []
|
102
102
|
rubyforge_project:
|
103
|
-
rubygems_version: 2.
|
103
|
+
rubygems_version: 2.5.1
|
104
104
|
signing_key:
|
105
105
|
specification_version: 4
|
106
106
|
summary: Update your gems and find their changelogs
|