gemfresh 1.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in gemfresh.gemspec
4
+ gemspec
data/MIT-LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Jon Williams, http://jonathannen.com/
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # Gemfresh
2
+ Scans your bundler Gemfile and lets you know how up-to-date your gems are.
3
+
4
+ ## Installation and Usage
5
+
6
+ To install, simply grab the gem:
7
+ gem install gemfresh
8
+
9
+ Change a project directory with a Gemfile (e.g. Your Rails v3+ project) and
10
+ type:
11
+ gemfresh
12
+
13
+ This will output a list of current, updateable and obsolete gems. For more
14
+ information on what this means, run gemfresh with the --help option:
15
+ gemfresh --help
16
+
17
+ ## License
18
+ MIT Licensed. See MIT-LICENSE.txt for more information.
19
+
20
+ Thanks [@jonathannen](http://twitter.com/jonathannen).
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/gemfresh ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+ require 'gemfresh'
data/gemfresh.gemspec ADDED
@@ -0,0 +1,17 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'gemfresh'
3
+ s.version = '1.0.1'
4
+ s.platform = Gem::Platform::RUBY
5
+ s.authors = ['Jon Williams']
6
+ s.email = ['jon@jonathannen.com']
7
+ s.homepage = 'https://github.com/jonathannen/gemfresh'
8
+ s.summary = 'Checks the freshness of your Gemfile.'
9
+ s.description = 'Scans Gemfiles to check for obsolete and updateable gems.'
10
+
11
+ s.files = `git ls-files`.split("\n")
12
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
13
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
+ s.require_paths = ['.']
15
+
16
+ s.add_runtime_dependency 'bundler', '~> 1.0.18'
17
+ end
data/gemfresh.rb ADDED
@@ -0,0 +1,234 @@
1
+ require 'net/http'
2
+ require 'rubygems'
3
+ require 'bundler'
4
+ require 'time'
5
+ require File.dirname(__FILE__) + '/support'
6
+
7
+ # Handle ARGV
8
+ if ARGV.include?('--help')
9
+ puts <<-HELP
10
+ Usage:
11
+ gemfresh [GEMFILE] [LOCKFILE]
12
+
13
+ Both GEMFILE and LOCKFILE will default to "Gemfile" and "Gemfile.lock" in
14
+ your current directory. Generally you'll simply invoke gemfresh from your
15
+ Rails (or similar) project directory.
16
+
17
+ Gemfresh will list three categories of gems. "Current" gems are up-to-date.
18
+ "Obsolete" gems
19
+
20
+ "Updateable" gems that have a 'fuzzy' gemspec - e.g. '~> 2.2.0' is a fuzzy
21
+ match for 2.2.1, 2.2.2, etc. Running bundle update will attempt to update
22
+ your gems. If something is listed at updateable, you have an older version
23
+ - e.g. "2.2.1", when the current is "2.2.2".
24
+
25
+ Just because a gem is updateable or obsolete, doesn't mean it can be
26
+ updated. There might be dependencies that limit you to specific versions.
27
+
28
+ Check the bundler documentation (http://gembundler.com/) for more
29
+ information on Gemfiles.
30
+ HELP
31
+ exit
32
+ end
33
+
34
+ # Check for gemfile and lockfiles
35
+ gemfile = ARGV[0] || './Gemfile'
36
+ lockfile = ARGV[1] || './Gemfile.lock'
37
+ unless File.exists?(gemfile)
38
+ puts "Couldn't find #{gemfile}.\nRun gemfresh with --help if you need more information."
39
+ exit
40
+ end
41
+ unless File.exists?(lockfile)
42
+ puts "Couldn't find #{lockfile}.\nRun gemfresh with --help if you need more information."
43
+ exit
44
+ end
45
+
46
+ # Front for RubyGems
47
+ class RubyGemReader < Struct.new('RubyGemReader', :uri)
48
+ def get(path, data={}, content_type='application/x-www-form-urlencoded')
49
+ request = Net::HTTP::Get.new(path)
50
+ request.add_field 'Connection', 'keep-alive'
51
+ request.add_field 'Keep-Alive', '30'
52
+ request.add_field 'User-Agent', 'github.com/jonathannen/gemfresh'
53
+ response = connection.request request
54
+ response.body
55
+ end
56
+ private
57
+ # A persistent connection
58
+ def connection(host = Gem.host)
59
+ return @connection unless @connection.nil?
60
+ @connection = Net::HTTP.new self.uri.host, self.uri.port
61
+ @connection.start
62
+ @connection
63
+ end
64
+ end
65
+
66
+ # Start in earnets
67
+ puts "Checking the freshness of your Gemfile.\n"
68
+
69
+ # Get the data from bundler
70
+ Bundler.settings[:frozen] = true
71
+ bundle = Bundler::Dsl.evaluate('./Gemfile', './Gemfile.lock', {})
72
+
73
+ # Set up the top level values
74
+ deps = bundle.dependencies
75
+ specs = bundle.resolve
76
+ sources = {}
77
+ results = { :current => [], :update => [], :obsolete => [] }
78
+ count = 0
79
+ prereleases = 0
80
+
81
+ # Map dependencies to their specs, then select RubyGem sources
82
+ dep_specs = deps.map { |dep| [dep, specs.find { |spec| spec.name == dep.name }] }
83
+ dep_specs = dep_specs.select { |dep, spec| !spec.nil? && (spec.source.class == Bundler::Source::Rubygems) }
84
+
85
+ # Do we have any deps?
86
+ if deps.empty?
87
+ puts "No top-level RubyGem dependencies found in your Gemfile.\nRun gemfresh with --help if you need more information."
88
+ exit
89
+ end
90
+
91
+ # Iterate through the deps, checking the spec against the latest version
92
+ print "Hitting up your RubyGems sources: "
93
+ dep_specs.each do |dep, spec|
94
+ name = dep.name
95
+ # version = spec.version.to_s
96
+
97
+ # Get a connection to the rubygem repository, reusing if we can
98
+ remote = spec.source.remotes.first
99
+ next if remote.nil?
100
+ reader = sources[remote]
101
+ reader = sources[remote] = RubyGemReader.new(remote) if reader.nil?
102
+
103
+ # Get the RubyGems data
104
+ # lookup = RubyGems.get("/api/v1/gems/#{name}.yaml")
105
+ gemdata = reader.get("/api/v1/gems/#{name}.yaml")
106
+ gemdata = YAML.load(gemdata)
107
+
108
+ # Get the versions list as well
109
+ versions = reader.get("/api/v1/versions/#{name}.yaml")
110
+ versions = YAML.load(versions)
111
+
112
+ # Store the result as a diff object
113
+ diff = SpecDiff.new(dep, spec, gemdata, versions)
114
+ results[diff.classify] << diff
115
+
116
+ # Stats
117
+ prereleases +=1 if diff.prerelease?
118
+ count += 1
119
+
120
+ # Get the dates of the given and current versions
121
+ # version_date = versions.find { |v| v['number'] == version }
122
+ # version_date = Time.parse(version_date['built_at']) unless version_date.nil?
123
+ # current_date = versions.find { |v| v['number'] == current }
124
+ # current_date = Time.parse(current_date['built_at']) unless current_date.nil?
125
+
126
+ # Exact match or directly updatable? If so, we can move on
127
+ # prerelease = false
128
+ # match = case
129
+ # when (version == current) then :current
130
+ # when (dep.match?(dep.name, current)) then :update
131
+ # else nil
132
+ # end
133
+ #
134
+ # # Not exact or updatable - we need to check if you're on a pre-release version
135
+ # if match.nil?
136
+ # match = :obsolete
137
+ # versions = versions.select { |v| v['prerelease']}.map { |v| v['number'] }
138
+ # prerelease = versions.include?(version)
139
+ # # If it's a prerelease determine what kind
140
+ # if prerelease
141
+ # prereleases += 1
142
+ # current = versions.first # Big assumption
143
+ # match = case
144
+ # when (version == current) then :current
145
+ # when (dep.match?(dep.name, current)) then :update
146
+ # else :obsolete
147
+ # end
148
+ # end
149
+ # end
150
+
151
+ # Got our result
152
+ # results[match] << SpecDiff.new(dep, spec, versions)
153
+ # [dep, spec, current, prerelease, version_date, current_date]
154
+ print "."
155
+ STDOUT.flush
156
+ end
157
+ puts " Done!"
158
+
159
+ # Let the user about prereleases
160
+ if prereleases > 0
161
+ puts "\nYou have #{prereleases} prerelease gem#{prereleases == 1 ? '' : 's'}. Prereleases will be marked with a '*'."
162
+ end
163
+
164
+ # Output Gem Ages
165
+ puts "\nThe following Gems are:"
166
+ ages = results.values.flatten.group_by(&:build_age)
167
+ {:month1 => 'less than a month', :month6 => '6 months or less', :year1 => 'less than a year', :more => 'more than a year'}.each_pair do |key, value|
168
+ next if ages[key].nil?
169
+ puts "-- #{value} old:"
170
+ puts ages[key].map(&:to_s).join(', ')
171
+ end
172
+
173
+ # Output Current Gems
174
+ if results[:current].empty?
175
+ puts "\nYou don't have any current gems."
176
+ else
177
+ puts "\nThe following gems at the most current version: "
178
+ puts results[:current].map(&:to_s).join(', ')
179
+ end
180
+
181
+ # Output Updatable Gems
182
+ if results[:update].empty?
183
+ puts "\nYou don't have any updatable gems."
184
+ else
185
+ puts "\nThe following gems are locked to older versions, but your Gemfile allows for the current version: "
186
+ results[:update].each do |diff|
187
+ puts " #{diff}, with #{diff.dep.requirement} could allow #{diff.version_available}"
188
+ end
189
+ puts "Barring dependency issues, these gems could be updated to current using 'bundle update'."
190
+ end
191
+
192
+ # Output Obsolete Gems
193
+ if results[:obsolete].empty?
194
+ puts "\nYou don't have any obsolete gems."
195
+ else
196
+ puts "\nThe following gems are obsolete: "
197
+ results[:obsolete].each do |diff|
198
+ released = diff.version_build_date(diff.version_available)
199
+ released = released.nil? ? '.' : ", #{released.strftime('%d %b %Y')}."
200
+
201
+ suggest = diff.suggest
202
+ suggest = suggest.nil? ? '' : "Also consider version #{suggest}."
203
+
204
+ puts " #{diff} is now at #{diff.version_available}#{released} #{suggest}"
205
+ end
206
+ end
207
+
208
+
209
+
210
+
211
+
212
+
213
+
214
+
215
+
216
+
217
+
218
+
219
+
220
+
221
+
222
+
223
+
224
+
225
+
226
+
227
+
228
+
229
+
230
+
231
+
232
+
233
+
234
+
data/support.rb ADDED
@@ -0,0 +1,84 @@
1
+ class SpecDiff < Struct.new(:dep, :spec, :gemdata, :versions)
2
+
3
+ # Configure the diff
4
+ def initialize(*args)
5
+ super
6
+ # Check for prerelease - Gem rules are that a letter indicates a prerelease
7
+ # See http://rubygems.rubyforge.org/rubygems-update/Gem/Version.html#method-i-prerelease-3F
8
+ @prerelease = version_in_use =~ /[a-zA-Z]/
9
+ end
10
+
11
+ # Return a :month1, :month6, :year1, :more depending on the
12
+ # build age of the available version
13
+ def build_age
14
+ build_date = version_build_date(version_in_use)
15
+ return :more if build_date.nil?
16
+ days = ((Time.now.utc - build_date)/(24 * 60 * 60)).round
17
+ case
18
+ when days < 31 then :month1
19
+ when days < 182 then :month6
20
+ when days < 366 then :year1
21
+ else :more
22
+ end
23
+ end
24
+
25
+ # Classify this as :current, :update or :obsolete
26
+ def classify
27
+ case
28
+ when (version_available == version_in_use) then :current
29
+ when (dep.match?(name, version_available)) then :update
30
+ else :obsolete
31
+ end
32
+ end
33
+
34
+ def prerelease?; @prerelease; end
35
+
36
+ def name; dep.name; end
37
+
38
+ # Is there a suggested version - e.g. if you're using rails 3.0.8, the most
39
+ # current might be 3.1.0. However, the suggested version would be 3.0.10 --
40
+ # this will suggest the best version within your current minor version tree.
41
+ # May return nil if you're at the current suggestion, or if there is no
42
+ # reasonable match
43
+ def suggest
44
+ match = nil
45
+ head = version_in_use.rpartition('.').first
46
+ versions.sort_by { |v| v['built_at'] }.reverse.each do |ver|
47
+ ver = ver['number']
48
+ match = ver and break if ver.start_with?(head)
49
+ end
50
+ (match == version_in_use) || (match == version_available) ? nil : match
51
+ end
52
+
53
+ # String representation is the basic spec form 'gename (version)',
54
+ # with a start appended for prereleases.
55
+ def to_s
56
+ "#{spec}#{prerelease? ? '*' : ''}"
57
+ end
58
+
59
+ # Return the version data for a given string
60
+ def version_data(version)
61
+ return nil if versions.nil? || versions.empty?
62
+ version = versions.find { |v| v['number'] == version }
63
+ end
64
+
65
+ # Return the build date for a given version string (e.g. '1.2.1')
66
+ def version_build_date(version)
67
+ return nil if versions.nil? || versions.empty?
68
+ version_date = version_data(version)['built_at']
69
+ version_date.nil? ? nil : Time.parse(version_date)
70
+ end
71
+
72
+ # Best version available according to RubyGems data
73
+ def version_available
74
+ return gemdata["version"].to_s unless prerelease?
75
+
76
+ # Depends if it's a prerelease or not
77
+ prereleases = versions.select { |v| v['prerelease']}.map { |v| v['number'] }
78
+ prereleases.first # Big Assumption, but appears correct on data so far
79
+ end
80
+
81
+ # The version currently in use according to the lockfile
82
+ def version_in_use; spec.version.to_s; end
83
+
84
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gemfresh
3
+ version: !ruby/object:Gem::Version
4
+ hash: 21
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 1
10
+ version: 1.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Jon Williams
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-09-29 00:00:00 +10:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: bundler
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 51
30
+ segments:
31
+ - 1
32
+ - 0
33
+ - 18
34
+ version: 1.0.18
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ description: Scans Gemfiles to check for obsolete and updateable gems.
38
+ email:
39
+ - jon@jonathannen.com
40
+ executables:
41
+ - gemfresh
42
+ extensions: []
43
+
44
+ extra_rdoc_files: []
45
+
46
+ files:
47
+ - .gitignore
48
+ - Gemfile
49
+ - MIT-LICENSE.txt
50
+ - README.md
51
+ - Rakefile
52
+ - bin/gemfresh
53
+ - gemfresh.gemspec
54
+ - gemfresh.rb
55
+ - support.rb
56
+ has_rdoc: true
57
+ homepage: https://github.com/jonathannen/gemfresh
58
+ licenses: []
59
+
60
+ post_install_message:
61
+ rdoc_options: []
62
+
63
+ require_paths:
64
+ - .
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ hash: 3
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ requirements: []
84
+
85
+ rubyforge_project:
86
+ rubygems_version: 1.6.2
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: Checks the freshness of your Gemfile.
90
+ test_files: []
91
+