gem-checkout 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ee28067ee5184c515081a3cfad38b70eda8c9bb7
4
+ data.tar.gz: e3a3abe9fa00964bfa733ead7414a266baa961ac
5
+ SHA512:
6
+ metadata.gz: 8a55d5ba004b3989f3020f892bf5112a1016a9d867a41ce16bc969c8e256511c674559cafa7e95d9ff3120ecbf34763c10628d5d542fafc93091a427f569ca38
7
+ data.tar.gz: 2ab5e1fb93f34b50f3e44899bc292765737960c99e3cc2c6060b5241fa30007d9a435376be850a2e8d5f95eb9de2ded837fc05ef02f7c51b604cb71788fb0c44
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Cezary Baginski
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,65 @@
1
+ # Gem::Checkout
2
+
3
+ RubyGems addon command ('checkout') which, given a gem, will checkout the matching sources at the same version.
4
+
5
+ Why? So you no longer have to work out where the source code for a given gem is.
6
+
7
+ (Which allows you to automate a LOT of tasks given just a gem name!).
8
+
9
+ For example:
10
+
11
+ ```bash
12
+ $ gem checkout nenv -v 0.2.0
13
+ ```
14
+
15
+ will checkout the GitHub source code (into the current dir) for the `nenv` gem at `0.2.0` (commit: beb9981)
16
+
17
+
18
+ ## Installation
19
+
20
+ Add this line to your application's Gemfile:
21
+
22
+ ```sh
23
+ gem install gem-checkout
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ Proof of concept right now, so open issues and/or PRs for improvements.
29
+
30
+ Options:
31
+
32
+ ```
33
+ -d LEVEL
34
+ ```
35
+ sets the debug level, where 0=debug, 1=warn, and so on...
36
+
37
+ ```
38
+ -v, --version VERSION
39
+ ```
40
+
41
+ The version of the gem to you want to hack on - the repo and revision will be discovered.
42
+
43
+ (If you don't provide this option, the latest version will be used).
44
+
45
+ ## FAQ
46
+
47
+ None yet, so open an issue...
48
+
49
+
50
+ ## Development
51
+
52
+ ```
53
+ bundle install
54
+ bundle exec guard
55
+ ```
56
+
57
+ To install this gem onto your local machine, run `bundle exec rake install`.
58
+
59
+ ## Contributing
60
+
61
+ Bug reports and pull requests are welcome on GitHub at https://github.com/e2/gem-checkout
62
+
63
+ ## License
64
+
65
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,15 @@
1
+ require 'logger'
2
+
3
+ module Gem
4
+ module Checkout
5
+ class << self
6
+ def logger=(logger)
7
+ @logger = logger
8
+ end
9
+
10
+ def logger
11
+ @logger ||= ::Logger.new(STDERR).tap { |logger| logger.level = Logger::WARN }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ module Gem
2
+ module Checkout
3
+ class Process
4
+ class Error < RuntimeError
5
+ class CommandFailed < Error
6
+ attr_reader :exit_code
7
+
8
+ def initialize(exit_code)
9
+ @exit_code = exit_code
10
+ end
11
+ end
12
+ end
13
+
14
+ def self.run(*args)
15
+ pid = Kernel.spawn(*args)
16
+ result = ::Process.wait2(pid)
17
+ exit_code = result.last.exitstatus
18
+ fail(Error::CommandFailed, exit_code) unless exit_code.zero?
19
+ end
20
+
21
+ def self.capture(*args)
22
+ IO.popen(args).read
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+ module Gem
2
+ module Checkout
3
+ module Repository
4
+ class Error < RuntimeError
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,55 @@
1
+ require 'gem-checkout/repository/git'
2
+
3
+
4
+ module Gem
5
+ module Checkout
6
+ module Repository
7
+ class GitHub
8
+ class Error < Repository::Error
9
+ class BadURI < Error
10
+ class NotGitHub < BadURI
11
+ end
12
+
13
+ class NoProjectName < BadURI
14
+ end
15
+ end
16
+ end
17
+
18
+ def initialize(vague_uri)
19
+ uri = vague_uri.dup
20
+ fail Error::BadURI::NotGitHub unless uri.host == 'github.com'
21
+
22
+
23
+ uri.scheme = 'https'
24
+ uri.port = nil
25
+ uri.userinfo = nil
26
+ uri.query = nil
27
+ uri.fragment = nil
28
+ uri = URI.parse(uri.to_s)
29
+
30
+ m = /(?<org>[[:alnum:]_.-]+)\/(?<project>[[:alnum:]_.-]+)/.match(uri.path)
31
+
32
+ fail Error::BadURI::NoProjectName unless m
33
+ org = m[:org]
34
+ project = m[:project]
35
+ uri.path = "/#{org}/#{project}.git"
36
+
37
+ @git = Git.new(uri)
38
+ end
39
+
40
+ def clone(*args)
41
+ @git.clone(*args)
42
+ end
43
+
44
+ def checkout(*args)
45
+ @git.checkout(*args)
46
+ end
47
+
48
+ def get_tag_ref(*args)
49
+ @git.get_tag_ref(*args)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
@@ -0,0 +1,58 @@
1
+ require 'gem-checkout/logger'
2
+ require 'gem-checkout/process'
3
+ require 'gem-checkout/repository'
4
+
5
+ module Gem
6
+ module Checkout
7
+ module Repository
8
+ class Git
9
+ class Error < Repository::Error
10
+ class SingleTagNotFound < Error
11
+ end
12
+
13
+ class CloneFailed < Error
14
+ end
15
+
16
+ class CheckoutFailed < Error
17
+ end
18
+
19
+ class FailedToFetchTags < Error
20
+ end
21
+ end
22
+
23
+ attr_reader :uri
24
+ def initialize(uri)
25
+ @uri = uri
26
+ end
27
+
28
+ def clone(options)
29
+ dir = options[:directory]
30
+ Gem::Checkout.logger.debug "Cloning: #{uri.to_s}"
31
+ Process.run('git', 'clone', uri.to_s, dir)
32
+ rescue Process::Error => ex
33
+ fail Error::CloneFailed, "Failed to clone #{uri.to_s} (#{ex.message})"
34
+ end
35
+
36
+ def checkout(ref)
37
+ Process.run('git', 'checkout', ref)
38
+ rescue Process::Error => ex
39
+ fail Error::CheckoutFailed, "Failed to checkout #{ref} (#{ex})"
40
+ end
41
+
42
+ def get_tag_ref(tag)
43
+ Gem::Checkout.logger.debug "Looking up tag: #{tag.inspect}"
44
+ Gem::Checkout.logger.debug "Getting tags from : #{uri.to_s}"
45
+ output = Process.capture('git', 'ls-remote', '--tags', uri.to_s)
46
+ tag_info = output.split("\n")
47
+ matching = tag_info.select { |details| details =~ /refs\/tags\/#{tag}$/}
48
+ unless matching.size == 1
49
+ fail Error::SingleTagNotFound, "Expected to match 1 tag #{tag}, matched: #{matching.inspect}"
50
+ end
51
+ matching.first.split("\t").first
52
+ rescue Process::Error => ex
53
+ fail Error::FailedToFetchTags, ex.message
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,123 @@
1
+ require 'ostruct'
2
+ require 'uri'
3
+ require 'logger'
4
+
5
+ require 'gem-checkout/repository/git-hub'
6
+ require 'gem-checkout/spec'
7
+
8
+ module Gem
9
+ module Checkout
10
+ class DataFinder
11
+ class Error < RuntimeError
12
+ class NoMatch < Error
13
+ end
14
+ end
15
+
16
+ class << self
17
+ def detect(klass, object, priorities)
18
+ found = nil
19
+ priorities.values.each do |fields|
20
+ found = traverse_find(klass, object, fields)
21
+ break if found
22
+ end
23
+ found
24
+ end
25
+
26
+ private
27
+
28
+ def traverse_find(klass, object, fields)
29
+ loop do
30
+ fields.each do |field|
31
+ url = object.public_send(field)
32
+ begin
33
+ return klass.new(url)
34
+ rescue Error::NoMatch
35
+ next
36
+ end
37
+ end
38
+
39
+ object = object.alternative
40
+ return nil unless object
41
+ next
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ class RepositoryInfo < DataFinder
48
+ PRIORITIES = {
49
+ recommended: %i(source_code_uri),
50
+ sufficient: %i(source_code_url repository_uri repository_url),
51
+ user_pages: %i(project_uri project_url homepage),
52
+ last_resort: %i(bug_tracker_uri bug_tracker_url)
53
+ }
54
+
55
+ def self.detect(object)
56
+ DataFinder.detect(self, object, PRIORITIES)
57
+ end
58
+
59
+ attr_reader :repository
60
+
61
+ def initialize(url)
62
+ fail Error::NoMatch if !url || url.empty?
63
+ Gem::Checkout.logger.debug "Found non-empty url: #{url}"
64
+ uri = URI.parse(url)
65
+ @repository = Repository::GitHub.new(uri)
66
+ Gem::Checkout.logger.debug "Detected GitHub repository url: #{uri}"
67
+ rescue Gem::Checkout::Repository::GitHub::Error::BadURI
68
+ fail Error::NoMatch
69
+ end
70
+ end
71
+
72
+ class RepositoryHash < DataFinder
73
+ PRIORITIES = {
74
+ recommended: %i(source_reference),
75
+ sufficient: %i(commit revision),
76
+ }
77
+
78
+ def self.detect(object)
79
+ DataFinder.detect(self, object, PRIORITIES)
80
+ end
81
+
82
+ attr_reader :reference
83
+
84
+ def initialize(reference)
85
+ fail Error::NoMatch unless (reference && !reference.empty?)
86
+ @reference = reference
87
+ Gem::Checkout.logger.debug "Found gem's commit info: #{reference}"
88
+ end
89
+ end
90
+
91
+ class Source
92
+ attr_reader :source_reference
93
+ attr_reader :repository
94
+
95
+ class Error < RuntimeError
96
+ class NoValidRepositoryFound
97
+ end
98
+ end
99
+
100
+ def initialize(name, version=nil)
101
+ Gem::Checkout.logger.debug "Gathering infor about #{name} (#{version || 'latest'})"
102
+ object = Spec.info(name, version)
103
+
104
+ @name = name
105
+
106
+ repository = RepositoryInfo.detect(object)
107
+ fail Error::NoValidRepositoryFound unless repository
108
+ @repository = repository.repository
109
+
110
+ reference = RepositoryHash.detect(object)
111
+
112
+ @source_reference =
113
+ if reference
114
+ reference.reference
115
+ else
116
+ Gem::Checkout.logger.warn "No metadata key with commit! Matching tag to version, which is insecure if you don't trust the repository owners!"
117
+ @repository.get_tag_ref("v#{object.version}")
118
+ end
119
+ # TODO: check integrity of gem vs source
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,238 @@
1
+ require 'ostruct'
2
+ require 'gems'
3
+
4
+ require 'gem-checkout/logger'
5
+
6
+ module Gem
7
+ module Checkout
8
+ class Spec
9
+ class Error < RuntimeError
10
+ class NoSuchGem < Error
11
+ end
12
+ end
13
+
14
+ module CommonMetadata
15
+ attr_reader :alternative
16
+ def initialize(data, alternative=nil)
17
+ @alternative = alternative
18
+ @data = OpenStruct.new(data)
19
+ end
20
+
21
+ def homepage
22
+ read(:homepage_uri)
23
+ end
24
+
25
+ private
26
+
27
+ def data
28
+ @data
29
+ end
30
+ end
31
+
32
+ class Local < Spec
33
+ class Metadata
34
+ include CommonMetadata
35
+
36
+ %i(
37
+ source_code_uri
38
+ source_code_url
39
+ repository_uri
40
+ repository_url
41
+ project_uri
42
+ project_url
43
+ source_reference
44
+ commit
45
+ revision
46
+ bug_tracker_uri
47
+ bug_tracker_url
48
+ ).each do |name|
49
+ define_method(name) do
50
+ read(name)
51
+ end
52
+ end
53
+
54
+
55
+ private
56
+
57
+ def read(key)
58
+ Gem::Checkout.logger.debug "Checking local metadata (#{key})"
59
+ data.public_send(key) if data.respond_to?(key)
60
+ end
61
+ end
62
+
63
+ def initialize(name, version, remote)
64
+ @data = find_by_name(name, version)
65
+ @name = name
66
+ @version = version
67
+ @remote = remote
68
+ end
69
+
70
+ # TODO: fake fields - just as proof of concept
71
+
72
+ %i(
73
+ source_code_uri
74
+ source_code_url
75
+ repository_uri
76
+ repository_url
77
+ project_uri
78
+ project_url
79
+ source_reference
80
+ commit
81
+ revision
82
+ ).each do |name|
83
+ define_method(name) do
84
+ read(name)
85
+ end
86
+ end
87
+
88
+ def homepage
89
+ read(:homepage)
90
+ end
91
+
92
+ def version
93
+ read(:version)
94
+ end
95
+
96
+ def alternative
97
+ Metadata.new(data.metadata, @remote)
98
+ end
99
+
100
+ private
101
+
102
+ def data
103
+ @data
104
+ end
105
+
106
+ def find_by_name(*args)
107
+ spec = Gem::Specification
108
+ return spec.find_by_name(*args) if spec.respond_to?(:find_by_name)
109
+ Gem.source_index.find_name(*args).last or raise Gem::LoadError
110
+ end
111
+
112
+ def read(key)
113
+ Gem::Checkout.logger.debug "Checking local info (#{key})"
114
+ data.public_send(key) if data.respond_to?(key)
115
+ end
116
+ end
117
+
118
+ class Remote < Spec
119
+ class Metadata
120
+ include CommonMetadata
121
+
122
+ %i(
123
+ source_code_uri
124
+ source_code_url
125
+ repository_uri
126
+ repository_url
127
+ project_uri
128
+ project_url
129
+ source_reference
130
+ commit
131
+ revision
132
+ bug_tracker_uri
133
+ bug_tracker_url
134
+ ).each do |name|
135
+ define_method(name) do
136
+ data.public_send(name) if data.respond_to?(name)
137
+ end
138
+ end
139
+
140
+ private
141
+
142
+ def read(key)
143
+ Gem::Checkout.logger.debug "Checking remote metadata (#{key})"
144
+ data.public_send(key) if data.respond_to?(key)
145
+ end
146
+ end
147
+
148
+ def initialize(name, version)
149
+ @data = nil
150
+ @name = name
151
+ @version = version
152
+
153
+ # @homepage = nil_when_empty(object.homepage)
154
+ # @homepage = nil_when_empty(object.homepage_uri)
155
+ end
156
+
157
+ %i(
158
+ source_code_uri
159
+ source_code_url
160
+ repository_uri
161
+ repository_url
162
+ project_uri
163
+ project_url
164
+ source_reference
165
+ commit
166
+ revision
167
+ bug_tracker_uri
168
+ bug_tracker_url
169
+ ).each do |name|
170
+ define_method(name) do
171
+ read(name)
172
+ end
173
+ end
174
+
175
+ def alternative
176
+ Metadata.new(data.metadata, nil)
177
+ end
178
+
179
+ def version
180
+ data.number
181
+ end
182
+
183
+ def homepage
184
+ base.homepage_uri
185
+ end
186
+
187
+ private
188
+
189
+ def base
190
+ @base ||=
191
+ begin
192
+ name = @name
193
+ Gem::Checkout.logger.debug "Looking up latest gem info ..."
194
+ result = Gems.info(name)
195
+ return OpenStruct.new(result) if result.is_a?(Hash)
196
+ fail Error::NoSuchGem, result
197
+ end
198
+ end
199
+
200
+ def data
201
+ @data ||= find_on_rubygems
202
+ end
203
+
204
+ def find_on_rubygems
205
+ name = @name
206
+ version = @version
207
+ return base if version.nil?
208
+
209
+ Gem::Checkout.logger.debug "Looking all gem versions ..."
210
+ versions = Gems.versions(name)
211
+ version = versions.detect do |info|
212
+ info['number'] == version
213
+ end
214
+
215
+ fail Error::NoSuchGem, "Could not find #{name} on rubygems.org" unless version
216
+ Gem::Checkout.logger.debug "Found info matching gem version #{@version}"
217
+ OpenStruct.new(version)
218
+ end
219
+
220
+ private
221
+
222
+ def read(key)
223
+ Gem::Checkout.logger.debug "Checking remote info (#{key})"
224
+ data.public_send(key) if data.respond_to?(key)
225
+ end
226
+ end
227
+
228
+ def self.info(name, version=nil)
229
+ remote = Spec::Remote.new(name, version)
230
+ Gem::Checkout.logger.debug "Checking for info locally ..."
231
+ Spec::Local.new(name, version, remote)
232
+ rescue Gem::LoadError
233
+ Gem::Checkout.logger.debug "Checking for info remotely ..."
234
+ remote
235
+ end
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,49 @@
1
+ require 'logger'
2
+
3
+ require 'gem-checkout/source'
4
+
5
+ class Gem::Commands::CheckoutCommand < Gem::Command
6
+ def initialize
7
+ super 'checkout', description
8
+ add_option('-v', '--version VERSION', 'version to checkout') do |version, options|
9
+ options[:version] = version
10
+ end
11
+
12
+ add_option('-d', '--debug LEVEL', 'set debug mode (0=debug)') do |level, options|
13
+ options[:debug_level] = Integer(level)
14
+ end
15
+ end
16
+
17
+ def arguments # :nodoc:
18
+ "checkout checkout the original repository at the same version"
19
+ end
20
+
21
+ def usage # :nodoc:
22
+ "#{program_name}"
23
+ end
24
+
25
+ def defaults_str # :nodoc:
26
+ ""
27
+ end
28
+
29
+ def description # :nodoc:
30
+ "Checkout a gem's repository or sources at the same version"
31
+ end
32
+
33
+ def execute
34
+ logger = Logger.new(STDERR)
35
+ logger.level = options[:debug_level] || Logger::WARN
36
+ Gem::Checkout.logger = logger
37
+
38
+ name = get_one_gem_name
39
+ source = Gem::Checkout::Source.new(name, options[:version])
40
+ repository = source.repository
41
+ repository.clone(directory: name)
42
+ Dir.chdir(name) do
43
+ repository.checkout(source.source_reference)
44
+ end
45
+ rescue Gem::Checkout::Repository::Error => ex
46
+ alert_error ex.message
47
+ terminate_interaction 1
48
+ end
49
+ end
@@ -0,0 +1,2 @@
1
+ require 'rubygems/command_manager'
2
+ Gem::CommandManager.instance.register_command :checkout
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gem-checkout
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ platform: ruby
6
+ authors:
7
+ - Cezary Baginski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: gems
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.8'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.10'
41
+ description: Uses gem's metadata or version to work out which commit to checkout
42
+ email:
43
+ - cezary@chronomantic.net
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - LICENSE.txt
49
+ - README.md
50
+ - lib/gem-checkout/logger.rb
51
+ - lib/gem-checkout/process.rb
52
+ - lib/gem-checkout/repository.rb
53
+ - lib/gem-checkout/repository/git-hub.rb
54
+ - lib/gem-checkout/repository/git.rb
55
+ - lib/gem-checkout/source.rb
56
+ - lib/gem-checkout/spec.rb
57
+ - lib/rubygems/commands/checkout_command.rb
58
+ - lib/rubygems_plugin.rb
59
+ homepage: https://github.com/e2/gem-checkout
60
+ licenses:
61
+ - MIT
62
+ metadata: {}
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubyforge_project:
79
+ rubygems_version: 2.4.5
80
+ signing_key:
81
+ specification_version: 4
82
+ summary: Gem command download and checkout repository at same version as gem
83
+ test_files: []