skippy 0.2.0.a → 0.3.0.a
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.gitmodules +3 -0
- data/.idea/.rakeTasks +6 -6
- data/.idea/codeStyleSettings.xml +8 -8
- data/.idea/encodings.xml +5 -5
- data/.idea/inspectionProfiles/Project_Default.xml +7 -7
- data/.idea/misc.xml +3 -3
- data/.idea/modules.xml +7 -7
- data/.idea/skippy.iml +89 -82
- data/.idea/vcs.xml +5 -5
- data/.rubocop.yml +93 -0
- data/.rubocop_todo.yml +24 -0
- data/.vscode/launch.json +51 -61
- data/.vscode/settings.json +41 -2
- data/.vscode/tasks.json +16 -16
- data/Gemfile +11 -2
- data/README.md +194 -15
- data/Rakefile +1 -1
- data/app/boot.rb +1 -1
- data/app/commands/install.rb +41 -0
- data/app/commands/lib.rb +42 -2
- data/app/commands/new.rb +22 -8
- data/app/commands/template.rb +2 -2
- data/bin/rubocop +17 -0
- data/bin/ruby-parse +17 -0
- data/bin/ruby-rewrite +17 -0
- data/debug/skippy.bat +2 -0
- data/fixtures/my_lib/{src → modules}/command.rb +0 -0
- data/fixtures/my_lib/{src → modules}/geometry.rb +0 -0
- data/fixtures/my_lib/modules/gl.rb +4 -0
- data/fixtures/my_lib/modules/gl/container.rb +8 -0
- data/fixtures/my_lib/modules/gl/control.rb +6 -0
- data/fixtures/my_lib/modules/gl/nested/nested.rb +8 -0
- data/fixtures/my_lib/{src → modules}/tool.rb +0 -0
- data/fixtures/my_lib/skippy.json +1 -1
- data/fixtures/my_project/skippy.json +2 -1
- data/fixtures/my_project/src/hello_world.rb +2 -2
- data/fixtures/my_project/src/hello_world/extension.json +9 -9
- data/fixtures/project_with_lib/.skippy/libs/my-lib/modules/command.rb +4 -0
- data/fixtures/project_with_lib/.skippy/libs/my-lib/modules/gl.rb +4 -0
- data/fixtures/project_with_lib/.skippy/libs/my-lib/modules/gl/container.rb +8 -0
- data/fixtures/project_with_lib/.skippy/libs/my-lib/modules/gl/control.rb +6 -0
- data/fixtures/project_with_lib/.skippy/libs/my-lib/skippy.json +5 -0
- data/fixtures/project_with_lib/.skippy/libs/my-other-lib/modules/something.rb +4 -0
- data/fixtures/project_with_lib/.skippy/libs/my-other-lib/skippy.json +5 -0
- data/fixtures/project_with_lib/skippy.json +25 -0
- data/fixtures/project_with_lib/skippy/commands/example.rb +14 -0
- data/fixtures/project_with_lib/src/hello_world.rb +47 -0
- data/fixtures/project_with_lib/src/hello_world/extension.json +10 -0
- data/fixtures/project_with_lib/src/hello_world/main.rb +21 -0
- data/fixtures/project_with_lib/src/hello_world/vendor/my-lib/command.rb +4 -0
- data/fixtures/project_with_lib/src/hello_world/vendor/my-other-lib/something.rb +4 -0
- data/lib/skippy.rb +2 -0
- data/lib/skippy/app.rb +2 -2
- data/lib/skippy/cli.rb +41 -20
- data/lib/skippy/command.rb +2 -4
- data/lib/skippy/config.rb +27 -22
- data/lib/skippy/config_accessors.rb +12 -12
- data/lib/skippy/group.rb +1 -3
- data/lib/skippy/helpers/file.rb +3 -3
- data/lib/skippy/installer.rb +49 -0
- data/lib/skippy/installer/git.rb +115 -0
- data/lib/skippy/installer/local.rb +19 -0
- data/lib/skippy/lib_module.rb +16 -16
- data/lib/skippy/lib_source.rb +139 -0
- data/lib/skippy/library.rb +50 -10
- data/lib/skippy/library_manager.rb +116 -18
- data/lib/skippy/module_manager.rb +104 -26
- data/lib/skippy/namespace.rb +17 -1
- data/lib/skippy/project.rb +34 -4
- data/lib/skippy/version.rb +3 -1
- data/skippy.gemspec +10 -5
- metadata +85 -29
- data/cSpell.json +0 -18
data/lib/skippy/app.rb
CHANGED
@@ -40,9 +40,9 @@ class Skippy::App
|
|
40
40
|
def templates
|
41
41
|
result = []
|
42
42
|
templates_source_path.entries.each { |entry|
|
43
|
-
template_path =
|
43
|
+
template_path = templates_source_path.join(entry)
|
44
44
|
next unless template_path.directory?
|
45
|
-
next if %w
|
45
|
+
next if %w(. ..).include?(entry.basename.to_s)
|
46
46
|
result << entry.expand_path(templates_source_path)
|
47
47
|
}
|
48
48
|
result
|
data/lib/skippy/cli.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
1
3
|
require 'skippy/app'
|
2
4
|
require 'skippy/command'
|
3
5
|
require 'skippy/group'
|
@@ -41,14 +43,15 @@ class Skippy::CLI < Skippy::Command
|
|
41
43
|
end
|
42
44
|
|
43
45
|
# Verbatim copy from Thor::Runner:
|
44
|
-
# Override Thor#help so it can give information about any class and any
|
46
|
+
# Override Thor#help so it can give information about any class and any
|
47
|
+
# method.
|
45
48
|
#
|
46
49
|
def help(meth = nil)
|
47
|
-
if meth && !
|
50
|
+
if meth && !respond_to?(meth)
|
48
51
|
initialize_thorfiles(meth)
|
49
52
|
klass, command = Thor::Util.find_class_and_command_by_namespace(meth)
|
50
53
|
self.class.handle_no_command_error(command, false) if klass.nil?
|
51
|
-
klass.start(['-h', command].compact, :
|
54
|
+
klass.start(['-h', command].compact, shell: shell)
|
52
55
|
else
|
53
56
|
super
|
54
57
|
end
|
@@ -58,18 +61,22 @@ class Skippy::CLI < Skippy::Command
|
|
58
61
|
# If a command is not found on Thor::Runner, method missing is invoked and
|
59
62
|
# Thor::Runner is then responsible for finding the command in all classes.
|
60
63
|
#
|
61
|
-
def method_missing(meth, *args)
|
64
|
+
def method_missing(meth, *args) # rubocop:disable Style/MethodMissing
|
62
65
|
meth = meth.to_s
|
63
66
|
initialize_thorfiles(meth)
|
64
67
|
klass, command = Thor::Util.find_class_and_command_by_namespace(meth)
|
65
68
|
self.class.handle_no_command_error(command, false) if klass.nil?
|
66
69
|
args.unshift(command) if command
|
67
|
-
klass.start(args, :
|
70
|
+
klass.start(args, shell: shell)
|
68
71
|
end
|
69
72
|
|
70
73
|
# Verbatim copy from Thor::Runner:
|
71
|
-
desc 'list [SEARCH]',
|
72
|
-
|
74
|
+
desc 'list [SEARCH]',
|
75
|
+
"List the available #{$PROGRAM_NAME} commands (--substring means .*SEARCH)"
|
76
|
+
method_options substring: :boolean,
|
77
|
+
group: :string,
|
78
|
+
all: :boolean,
|
79
|
+
debug: :boolean
|
73
80
|
def list(search = '')
|
74
81
|
initialize_thorfiles
|
75
82
|
|
@@ -77,15 +84,13 @@ class Skippy::CLI < Skippy::Command
|
|
77
84
|
search = /^#{search}.*/i
|
78
85
|
group = options[:group] || 'standard'
|
79
86
|
|
80
|
-
klasses = Thor::Base.subclasses.select
|
87
|
+
klasses = Thor::Base.subclasses.select { |k|
|
81
88
|
(options[:all] || k.group == group) && k.namespace =~ search
|
82
|
-
|
89
|
+
}
|
83
90
|
|
84
91
|
display_klasses(false, false, klasses)
|
85
92
|
end
|
86
93
|
|
87
|
-
private
|
88
|
-
|
89
94
|
# Based on Thor::Runner, with exception of program name.
|
90
95
|
def self.banner(command, all = false, subcommand = false)
|
91
96
|
"#{$PROGRAM_NAME} " + command.formatted_usage(self, all, subcommand)
|
@@ -96,6 +101,8 @@ class Skippy::CLI < Skippy::Command
|
|
96
101
|
true
|
97
102
|
end
|
98
103
|
|
104
|
+
private
|
105
|
+
|
99
106
|
# This is one of the places this runner differ from Thor::Runner. It will
|
100
107
|
# instead load files for the current project.
|
101
108
|
#
|
@@ -107,7 +114,16 @@ class Skippy::CLI < Skippy::Command
|
|
107
114
|
return unless project.exist?
|
108
115
|
project.command_files { |filename|
|
109
116
|
unless Thor::Base.subclass_files.keys.include?(File.expand_path(filename))
|
110
|
-
|
117
|
+
begin
|
118
|
+
Thor::Util.load_thorfile(filename, nil, options[:debug])
|
119
|
+
rescue ScriptError, StandardError => error
|
120
|
+
command_path = Pathname.new(filename).relative_path_from(project.path)
|
121
|
+
say "Error loading: #{command_path} (#{error})", :red
|
122
|
+
if options[:debug]
|
123
|
+
say error.inspect, :red
|
124
|
+
say error.backtrace.join("\n"), :red
|
125
|
+
end
|
126
|
+
end
|
111
127
|
end
|
112
128
|
}
|
113
129
|
end
|
@@ -120,15 +136,20 @@ class Skippy::CLI < Skippy::Command
|
|
120
136
|
end
|
121
137
|
|
122
138
|
# Based on Thor::Runner:
|
123
|
-
def display_klasses(_with_modules = false,
|
139
|
+
def display_klasses(_with_modules = false,
|
140
|
+
show_internal = false,
|
141
|
+
klasses = Thor::Base.subclasses)
|
142
|
+
|
124
143
|
unless show_internal
|
125
144
|
klasses -= [
|
126
145
|
Thor, Thor::Runner, Thor::Group,
|
127
|
-
Skippy, Skippy::CLI, Skippy::Command, Skippy::Command::Group
|
146
|
+
Skippy, Skippy::CLI, Skippy::Command, Skippy::Command::Group,
|
128
147
|
]
|
129
148
|
end
|
130
149
|
|
131
|
-
|
150
|
+
if klasses.empty?
|
151
|
+
raise Error, "No #{$PROGRAM_NAME.capitalize} commands available"
|
152
|
+
end
|
132
153
|
|
133
154
|
list = Hash.new { |h, k| h[k] = [] }
|
134
155
|
groups = klasses.select { |k| k.ancestors.include?(Thor::Group) }
|
@@ -177,16 +198,16 @@ class Skippy::CLI < Skippy::Command
|
|
177
198
|
# TODO(thomthom): Because of the odd issue with col_width mentioned in
|
178
199
|
# `display_klasses` the table isn't truncated. Can probably re-enable if
|
179
200
|
# the col_width issue is fixed.
|
180
|
-
#print_table(list, :truncate => true, :indent => 2, :colwidth => col_width)
|
201
|
+
# print_table(list, :truncate => true, :indent => 2, :colwidth => col_width)
|
181
202
|
width = (col_width + 2) * 2
|
182
|
-
print_table(list, :
|
203
|
+
print_table(list, indent: 2, colwidth: width)
|
183
204
|
end
|
184
|
-
|
205
|
+
alias display_tasks display_commands
|
185
206
|
|
186
207
|
# Based on Thor::Runner, skipping the yaml stuff:
|
187
208
|
def show_modules
|
188
|
-
info
|
189
|
-
labels = %w
|
209
|
+
info = []
|
210
|
+
labels = %w(Modules Namespaces)
|
190
211
|
|
191
212
|
info << labels
|
192
213
|
info << ['-' * labels[0].size, '-' * labels[1].size]
|
data/lib/skippy/command.rb
CHANGED
@@ -3,12 +3,10 @@ require 'thor'
|
|
3
3
|
module Skippy
|
4
4
|
class Command < Thor
|
5
5
|
|
6
|
-
protected
|
7
|
-
|
8
6
|
# Customize the banner as we don't care for the 'skippy' prefix for each
|
9
7
|
# item in the list.
|
10
|
-
def self.banner(command,
|
11
|
-
|
8
|
+
def self.banner(command, _namespace = nil, subcommand = false)
|
9
|
+
command.formatted_usage(self, true, subcommand).to_s
|
12
10
|
end
|
13
11
|
|
14
12
|
end
|
data/lib/skippy/config.rb
CHANGED
@@ -5,7 +5,7 @@ require 'skippy/error'
|
|
5
5
|
|
6
6
|
class Skippy::Config < Hash
|
7
7
|
|
8
|
-
|
8
|
+
attr_reader :path
|
9
9
|
|
10
10
|
class MissingPathError < Skippy::Error; end
|
11
11
|
|
@@ -14,25 +14,11 @@ class Skippy::Config < Hash
|
|
14
14
|
json = File.read(path)
|
15
15
|
config = JSON.parse(json,
|
16
16
|
symbolize_names: true,
|
17
|
-
object_class: self
|
18
|
-
)
|
17
|
+
object_class: self)
|
19
18
|
else
|
20
|
-
config =
|
19
|
+
config = new
|
21
20
|
end
|
22
|
-
|
23
|
-
config.merge!(defaults) { |_key, value, default|
|
24
|
-
if value.is_a?(Hash) && default.is_a?(Hash)
|
25
|
-
# Deep merge in order to merge nested hashes.
|
26
|
-
# Note: This currently doesn't merge arrays.
|
27
|
-
# http://stackoverflow.com/a/9381776/486990
|
28
|
-
merger = proc { |_k, v1, v2|
|
29
|
-
Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2
|
30
|
-
}
|
31
|
-
default.merge(value, &merger)
|
32
|
-
else
|
33
|
-
value || default
|
34
|
-
end
|
35
|
-
}
|
21
|
+
config.merge_defaults(defaults)
|
36
22
|
config.path = path
|
37
23
|
config
|
38
24
|
end
|
@@ -77,7 +63,7 @@ class Skippy::Config < Hash
|
|
77
63
|
if hash.keys.first.is_a?(String)
|
78
64
|
update_from_key_paths(hash)
|
79
65
|
else
|
80
|
-
|
66
|
+
deep_merge!(hash)
|
81
67
|
end
|
82
68
|
self
|
83
69
|
end
|
@@ -86,11 +72,30 @@ class Skippy::Config < Hash
|
|
86
72
|
"#{super}:#{self.class.name}"
|
87
73
|
end
|
88
74
|
|
75
|
+
# @param [Hash] defaults
|
76
|
+
def merge_defaults(defaults)
|
77
|
+
merge!(defaults) { |_key, value, default|
|
78
|
+
if value.is_a?(Hash) && default.is_a?(Hash)
|
79
|
+
# Deep merge in order to merge nested hashes.
|
80
|
+
# Note: This currently doesn't merge arrays.
|
81
|
+
# http://stackoverflow.com/a/9381776/486990
|
82
|
+
merger = proc { |_k, v1, v2|
|
83
|
+
v1.is_a?(Hash) && v2.is_a?(Hash) ? v1.merge(v2, &merger) : v2
|
84
|
+
}
|
85
|
+
default.merge(value, &merger)
|
86
|
+
else
|
87
|
+
# TODO(thomthom): Should `merger` include this logic?
|
88
|
+
value || default
|
89
|
+
end
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
89
93
|
private
|
90
94
|
|
91
|
-
|
95
|
+
# @param [Hash] hash
|
96
|
+
def deep_merge!(hash)
|
92
97
|
merger = proc { |_key, v1, v2|
|
93
|
-
Hash
|
98
|
+
v1.is_a?(Hash) && v2.is_a?(Hash) ? v1.merge(v2, &merger) : v2
|
94
99
|
}
|
95
100
|
merge!(hash, &merger)
|
96
101
|
end
|
@@ -105,7 +110,7 @@ class Skippy::Config < Hash
|
|
105
110
|
if key_path.is_a?(Symbol)
|
106
111
|
[key_path]
|
107
112
|
else
|
108
|
-
key_path.split('/').map
|
113
|
+
key_path.split('/').map(&:intern)
|
109
114
|
end
|
110
115
|
end
|
111
116
|
|
@@ -5,38 +5,38 @@ module Skippy::ConfigAccessors
|
|
5
5
|
|
6
6
|
private
|
7
7
|
|
8
|
-
def config_attr(*symbols, key: nil, type: nil)
|
9
|
-
config_attr_reader(*symbols, key: key, type: type)
|
8
|
+
def config_attr(*symbols, key: nil, default: nil, type: nil)
|
9
|
+
config_attr_reader(*symbols, key: key, type: type, default: default)
|
10
10
|
config_attr_writer(*symbols, key: key, type: type)
|
11
11
|
nil
|
12
12
|
end
|
13
13
|
|
14
|
-
def config_attr_reader(*symbols, key: nil, type: nil)
|
15
|
-
|
14
|
+
def config_attr_reader(*symbols, key: nil, default: nil, type: nil)
|
15
|
+
class_eval do
|
16
16
|
symbols.each { |symbol|
|
17
17
|
raise TypeError unless symbol.is_a?(Symbol)
|
18
|
-
define_method(symbol)
|
19
|
-
value = @config.get(key || symbol)
|
18
|
+
define_method(symbol) do
|
19
|
+
value = @config.get(key || symbol, default)
|
20
20
|
value = type.new(value) if type && !value.is_a?(type)
|
21
21
|
value
|
22
|
-
|
22
|
+
end
|
23
23
|
}
|
24
|
-
|
24
|
+
end
|
25
25
|
nil
|
26
26
|
end
|
27
27
|
|
28
28
|
def config_attr_writer(*symbols, key: nil, type: nil)
|
29
|
-
|
29
|
+
class_eval do
|
30
30
|
symbols.each { |symbol|
|
31
31
|
raise TypeError unless symbol.is_a?(Symbol)
|
32
32
|
symbol_set = "#{symbol}=".intern
|
33
|
-
define_method(symbol_set)
|
33
|
+
define_method(symbol_set) do |value|
|
34
34
|
value = type.new(value) if type && !value.is_a?(type)
|
35
35
|
@config.set(key || symbol, value)
|
36
36
|
value
|
37
|
-
|
37
|
+
end
|
38
38
|
}
|
39
|
-
|
39
|
+
end
|
40
40
|
nil
|
41
41
|
end
|
42
42
|
|
data/lib/skippy/group.rb
CHANGED
@@ -5,12 +5,10 @@ require 'skippy/command'
|
|
5
5
|
module Skippy
|
6
6
|
class Command::Group < Thor::Group
|
7
7
|
|
8
|
-
protected
|
9
|
-
|
10
8
|
# Customize the banner as we don't care for the 'skippy' prefix for each
|
11
9
|
# item in the list.
|
12
10
|
def self.banner
|
13
|
-
|
11
|
+
self_command.formatted_usage(self, false).to_s
|
14
12
|
end
|
15
13
|
|
16
14
|
end
|
data/lib/skippy/helpers/file.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
module Skippy::Helpers
|
2
2
|
module File
|
3
3
|
|
4
|
+
extend self
|
5
|
+
|
4
6
|
# @param [Pathname]
|
5
7
|
# @return [Array<Pathname>]
|
6
8
|
def directories(pathname)
|
7
9
|
return [] unless pathname.exist?
|
8
|
-
pathname.children.select
|
9
|
-
child.directory?
|
10
|
-
}
|
10
|
+
pathname.children.select(&:directory?)
|
11
11
|
end
|
12
12
|
|
13
13
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'skippy/lib_source'
|
2
|
+
require 'skippy/library'
|
3
|
+
require 'skippy/project'
|
4
|
+
|
5
|
+
class Skippy::LibraryInstaller
|
6
|
+
|
7
|
+
attr_reader :project, :source
|
8
|
+
|
9
|
+
# @param [Skippy::Project] project
|
10
|
+
# @param [Skippy::LibrarySource] source
|
11
|
+
def initialize(project, lib_source)
|
12
|
+
@project = project
|
13
|
+
@source = lib_source
|
14
|
+
@messager = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_status(&block)
|
18
|
+
@messager = block
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Skippy::Library]
|
22
|
+
def install
|
23
|
+
raise NotImplementedError
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# @param [Symbol] type
|
29
|
+
# @param [String] message
|
30
|
+
def status(type, message)
|
31
|
+
@messager.call(type, message) if @messager
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param [String] message
|
35
|
+
def info(message)
|
36
|
+
status(:info, message)
|
37
|
+
end
|
38
|
+
|
39
|
+
# @param [String] message
|
40
|
+
def warning(message)
|
41
|
+
status(:warning, "Warning: #{message}")
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Pathname]
|
45
|
+
def path
|
46
|
+
project.libraries.path
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'git'
|
2
|
+
require 'naturally'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
require 'skippy/error'
|
6
|
+
require 'skippy/installer'
|
7
|
+
require 'skippy/library'
|
8
|
+
|
9
|
+
module Skippy
|
10
|
+
|
11
|
+
class BranchNotFound < Skippy::Error; end
|
12
|
+
class TagNotFound < Skippy::Error; end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
class Skippy::GitLibraryInstaller < Skippy::LibraryInstaller
|
17
|
+
|
18
|
+
# @return [Skippy::Library]
|
19
|
+
def install
|
20
|
+
info "Installing #{source.basename} from #{source.origin}..."
|
21
|
+
target = path.join(source.lib_path)
|
22
|
+
previous_commit = nil
|
23
|
+
if target.directory?
|
24
|
+
git, previous_commit = update_repository(target)
|
25
|
+
else
|
26
|
+
git = clone_repository(source.origin, target)
|
27
|
+
end
|
28
|
+
begin
|
29
|
+
checkout_branch(git, source.branch) if source.branch
|
30
|
+
checkout_tag(git, source.requirement) unless edge_version?(source.requirement)
|
31
|
+
rescue Skippy::Error
|
32
|
+
git.checkout(previous_commit) if previous_commit
|
33
|
+
raise
|
34
|
+
end
|
35
|
+
library = Skippy::Library.new(target, source: source)
|
36
|
+
library
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# @param [URI] uri
|
42
|
+
# @param [Pathname] target
|
43
|
+
# @return [Git::Base]
|
44
|
+
def clone_repository(uri, target)
|
45
|
+
info 'Cloning...'
|
46
|
+
Git.clone(uri, target.basename, path: target.parent)
|
47
|
+
end
|
48
|
+
|
49
|
+
# @param [Pathname] target
|
50
|
+
# @return [Array(Git::Base, Git::Commit)]
|
51
|
+
def update_repository(target)
|
52
|
+
info 'Updating...'
|
53
|
+
library = Skippy::Library.new(target)
|
54
|
+
info "Current version: #{library.version}"
|
55
|
+
git = Git.open(target)
|
56
|
+
previous_commit = git.object('HEAD^').class
|
57
|
+
git.reset_hard
|
58
|
+
git.pull
|
59
|
+
[git, previous_commit]
|
60
|
+
end
|
61
|
+
|
62
|
+
# @param [Git::Base]
|
63
|
+
# @param [String] branch
|
64
|
+
def checkout_branch(git, branch)
|
65
|
+
branches = git.braches.map(&:name)
|
66
|
+
info "Branches: #{branches.inspect}"
|
67
|
+
unless branches.include?(branch)
|
68
|
+
raise Skippy::BranchNotFound, "Found no branch named: '#{branch}'"
|
69
|
+
end
|
70
|
+
git.checkout(branch)
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
|
74
|
+
# @param [Git::Base]
|
75
|
+
# @param [String] version
|
76
|
+
def checkout_tag(git, version)
|
77
|
+
tags = Naturally.sort_by(git.tags, :name)
|
78
|
+
tag = latest_version?(version) ? tags.last : resolve_tag(tags, version)
|
79
|
+
raise Skippy::TagNotFound, "Found no version: '#{version}'" if tag.nil?
|
80
|
+
git.checkout(tag)
|
81
|
+
# Verify the library version with the tagged version.
|
82
|
+
target = path.join(source.lib_path)
|
83
|
+
library = Skippy::Library.new(target)
|
84
|
+
unless library.version.casecmp(tag.name).zero?
|
85
|
+
warning "skippy.json version (#{library.version}) differ from "\
|
86
|
+
"tagged version (#{tag.name})"
|
87
|
+
end
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
|
91
|
+
# Resolve version numbers like RubyGem.
|
92
|
+
#
|
93
|
+
# @param [Array<Git::Tag>] tags List of tags sorted with newest first
|
94
|
+
# @param [String] version
|
95
|
+
# @return [Git::Tag]
|
96
|
+
def resolve_tag(tags, version)
|
97
|
+
requirement = Gem::Requirement.new(version)
|
98
|
+
tags.reverse.find { |tag|
|
99
|
+
next false unless Gem::Version.correct?(tag.name)
|
100
|
+
tag_version = Gem::Version.new(tag.name)
|
101
|
+
requirement.satisfied_by?(tag_version)
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
# @param [String] version
|
106
|
+
def edge_version?(version)
|
107
|
+
version && version.casecmp('edge').zero?
|
108
|
+
end
|
109
|
+
|
110
|
+
# @param [String] version
|
111
|
+
def latest_version?(version)
|
112
|
+
version.nil? || version.casecmp('latest').zero?
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|