new 0.1.1 → 1.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +13 -5
- data/bin/new +0 -2
- data/lib/new.rb +125 -52
- data/lib/new/cli.rb +514 -67
- data/lib/new/source.rb +110 -0
- data/lib/new/task.rb +67 -24
- data/lib/new/validation.rb +191 -0
- metadata +75 -132
- data/.gitignore +0 -6
- data/.new +0 -23
- data/.rspec +0 -2
- data/Gemfile +0 -16
- data/Gemfile.lock +0 -82
- data/Guardfile +0 -16
- data/LICENSE.txt +0 -22
- data/README.md +0 -128
- data/lib/new/core.rb +0 -7
- data/lib/new/dsl.rb +0 -42
- data/lib/new/interpolate.rb +0 -106
- data/lib/new/project.rb +0 -34
- data/lib/new/template.rb +0 -67
- data/lib/new/version.rb +0 -54
- data/spec/fixtures/custom/.new +0 -16
- data/spec/fixtures/custom/tasks/custom_bar_task/custom_bar_task.rb +0 -3
- data/spec/fixtures/custom/templates/custom_bar_template/.new +0 -3
- data/spec/fixtures/custom/templates/custom_bar_template/custom_bar.txt +0 -0
- data/spec/fixtures/project/.new +0 -4
- data/spec/fixtures/project/.new_cli_release_spec +0 -2
- data/spec/fixtures/tasks/custom_bar_task/custom_bar_task.rb +0 -1
- data/spec/fixtures/tasks/foo_task/Gemfile +0 -5
- data/spec/fixtures/tasks/foo_task/foo_task.rb +0 -7
- data/spec/fixtures/templates/custom_bar_template/.gitkeep +0 -0
- data/spec/fixtures/templates/foo_template/.new +0 -1
- data/spec/fixtures/templates/foo_template/[FOO.BAR].txt.erb +0 -1
- data/spec/fixtures/templates/foo_template/nested_[FOO.BAR]/foo.txt.erb +0 -1
- data/spec/lib/new/cli_spec.rb +0 -107
- data/spec/lib/new/interpolate_spec.rb +0 -43
- data/spec/lib/new/project_spec.rb +0 -33
- data/spec/lib/new/task_spec.rb +0 -39
- data/spec/lib/new/template_spec.rb +0 -59
- data/spec/lib/new/version_spec.rb +0 -26
- data/spec/lib/new_spec.rb +0 -19
- data/spec/spec_helper.rb +0 -46
- data/tasks/gem/.gemspec.erb +0 -4
- data/tasks/gem/README.md +0 -36
- data/tasks/gem/gem.rb +0 -170
- data/tasks/gem/gem_spec.rb +0 -138
- data/templates/js/.bowerrc +0 -3
- data/templates/js/.gitignore +0 -3
- data/templates/js/.new.erb +0 -11
- data/templates/js/CHANGELOG.md +0 -3
- data/templates/js/Gemfile +0 -2
- data/templates/js/Guardfile +0 -7
- data/templates/js/LICENSE-MIT.erb +0 -22
- data/templates/js/README.md.erb +0 -41
- data/templates/js/bower.json.erb +0 -11
- data/templates/js/demo/[PROJECT.FILENAME]_demo.coffee +0 -0
- data/templates/js/demo/[PROJECT.FILENAME]_demo.sass +0 -0
- data/templates/js/demo/index.html.erb +0 -12
- data/templates/js/lib/README.md +0 -2
- data/templates/js/package.json +0 -5
- data/templates/js/spec/[PROJECT.FILENAME]_spec.coffee.erb +0 -1
- data/templates/js/spec/[PROJECT.FILENAME]_spec.sass +0 -0
- data/templates/js/spec/index.html.erb +0 -35
- data/templates/js/src/[PROJECT.FILENAME].coffee.erb +0 -18
- data/templates/js/src/[PROJECT.FILENAME].sass +0 -0
- data/templates/js/testem.yml +0 -23
- data/templates/js/yuyi_menu +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZjNlNjUyMWY0NjY3ZjBjZThkZTM0MjQwODU1YTllYTNhNjYwZDBlZA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NmQ4NmQ4NTUwZWU3ZTAwNmFkZTJkMjUzOTYwN2Y0M2FjMDc2MzExZg==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NmE3NjBmODFiNDMxODkzNTFmYTAzMjA5YTlhMzEwZWQ4NDU4MmI0MzhiOTJm
|
10
|
+
NzczOGVjOWY2Yzk2NzQ5NTI5YThhMGJkZDBiN2U3MzJkNTA4MDFjYTQ1N2Fk
|
11
|
+
NjM2MzZkZDFhYTUwZDQ3MzFiY2RlZjZjMGQwZmZhZGE4MDlmM2Q=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MzlhMWQ4OWFjYmNhMTQ5YzYxMWNiM2I4OGVlNDA3ZmRkNzViMzM5ODU4ZTYz
|
14
|
+
MTU0NmIyMGU3YTgwNmFlOGVlNTQ0M2Y5YTA1ZGZlMmU2YmZkODcyMWQ4YzRl
|
15
|
+
MDI2NzE4ODRlZDJjZDFiNDI1NWY0NjIzMzcxZjZmMDc2ZDFjNDc=
|
data/bin/new
CHANGED
data/lib/new.rb
CHANGED
@@ -1,71 +1,144 @@
|
|
1
|
+
require 'active_support/core_ext/hash/deep_merge'
|
1
2
|
require 'yaml'
|
2
3
|
|
4
|
+
# Allow true/false to respond to Boolean class
|
5
|
+
module Boolean; end
|
6
|
+
class TrueClass; include Boolean; end
|
7
|
+
class FalseClass; include Boolean; end
|
8
|
+
|
3
9
|
class New
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def self.tasks
|
14
|
-
custom_tasks | default_tasks
|
15
|
-
end
|
10
|
+
require 'new/validation'
|
11
|
+
|
12
|
+
require 'new/cli'
|
13
|
+
require 'new/source'
|
14
|
+
require 'new/task'
|
15
|
+
|
16
|
+
HOME_DIRECTORY = ENV['HOME']
|
17
|
+
PROJECT_DIRECTORY = Dir.pwd
|
18
|
+
NEWFILE_NAME = 'Newfile'
|
16
19
|
|
17
|
-
# List all the available templates
|
18
20
|
#
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
# CLASS METHODS
|
22
|
+
#
|
23
|
+
class << self
|
24
|
+
@@cli = false
|
25
|
+
@@verbose = false
|
26
|
+
@@new_object = {
|
27
|
+
:sources => {
|
28
|
+
:default => 'brewster1134/new-tasks'
|
29
|
+
}
|
30
|
+
}
|
22
31
|
|
23
|
-
|
24
|
-
|
25
|
-
end
|
32
|
+
# access the current new object
|
33
|
+
def new_object; @@new_object; end
|
26
34
|
|
27
|
-
|
28
|
-
|
29
|
-
end
|
35
|
+
# set cli to true when initialized via cli
|
36
|
+
def set_cli; @@cli = true; end
|
30
37
|
|
31
|
-
|
32
|
-
|
33
|
-
|
38
|
+
# set verbose to true when set via cli
|
39
|
+
def set_verbose; @@verbose = true; end
|
40
|
+
def verbose; @@verbose; end
|
34
41
|
|
35
|
-
|
36
|
-
|
37
|
-
|
42
|
+
# Load Newfile in home & project directory
|
43
|
+
#
|
44
|
+
def load_newfiles
|
45
|
+
load_newfile File.join(HOME_DIRECTORY, NEWFILE_NAME)
|
46
|
+
load_newfile File.join(PROJECT_DIRECTORY, NEWFILE_NAME)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Merge symbolized hash data into global new object
|
50
|
+
#
|
51
|
+
# @param hash [Hash] A hash to be merged into existing data
|
52
|
+
#
|
53
|
+
# @return [Hash] New merged data
|
54
|
+
#
|
55
|
+
def new_object= hash
|
56
|
+
@@new_object.deep_merge! hash.deep_symbolize_keys
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# Load newfile contents into the global new object
|
62
|
+
#
|
63
|
+
# @param newfile_path [String] Path to a valid Newfile
|
64
|
+
#
|
65
|
+
# @return [Hash] Hash of Newfile yaml contents
|
66
|
+
#
|
67
|
+
def load_newfile newfile_path
|
68
|
+
# check file exists
|
69
|
+
return false unless File.file? newfile_path
|
38
70
|
|
39
|
-
|
40
|
-
|
71
|
+
# load Newfile yaml into global new_object hash
|
72
|
+
self.new_object = YAML.load File.read newfile_path
|
73
|
+
end
|
41
74
|
end
|
42
75
|
|
43
76
|
private
|
44
77
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
when :custom
|
50
|
-
Dir[File.join(CUSTOM_DIR, dir, '**')]
|
51
|
-
end.map{ |d| File.basename(d).to_sym }
|
52
|
-
end
|
53
|
-
end
|
78
|
+
def initialize version, changelog, *skip_tasks
|
79
|
+
# load newfiles and sources
|
80
|
+
New.load_newfiles unless @@cli
|
81
|
+
New::Source.load_sources
|
54
82
|
|
55
|
-
#
|
56
|
-
|
83
|
+
# update options with new attributes
|
84
|
+
@@new_object[:version] = version
|
85
|
+
@@new_object[:changelog] = changelog
|
57
86
|
|
58
|
-
#
|
59
|
-
|
60
|
-
|
61
|
-
|
87
|
+
# create new options to pass to task
|
88
|
+
new_options = @@new_object.dup
|
89
|
+
new_options.delete(:sources)
|
90
|
+
new_options.delete(:tasks)
|
62
91
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
require 'new/task'
|
92
|
+
new_tasks = []
|
93
|
+
@@new_object[:tasks].each do |task_name, task_options|
|
94
|
+
# skip tasks
|
95
|
+
next if skip_tasks.include? task_name.to_s
|
68
96
|
|
69
|
-
|
70
|
-
|
97
|
+
S.ay "Preparing `#{task_name}`", :header
|
98
|
+
|
99
|
+
# dupe task options
|
100
|
+
new_task_options = task_options ? task_options.dup : {}
|
101
|
+
|
102
|
+
# lookup and add task to array
|
103
|
+
task = New::Source.find_task task_name, new_task_options.delete(:source)
|
104
|
+
new_tasks << task
|
105
|
+
|
106
|
+
# add task options
|
107
|
+
new_options[:task_options] = new_task_options
|
108
|
+
|
109
|
+
# set options
|
110
|
+
task.options = new_options.dup
|
111
|
+
|
112
|
+
# validate task before running anything
|
113
|
+
S.ay 'Validating Options: ', :highlight_key
|
114
|
+
task.validate
|
115
|
+
S.ay 'OK', :highlight_value
|
116
|
+
|
117
|
+
# verify tasks
|
118
|
+
S.ay 'Verifying Task Dependencies: ', :highlight_key
|
119
|
+
task.verify
|
120
|
+
S.ay 'OK', :highlight_value
|
121
|
+
S.ay
|
122
|
+
end
|
123
|
+
|
124
|
+
# write new Newfile with new version
|
125
|
+
new_newfile = YAML.load(File.read(File.join(PROJECT_DIRECTORY, NEWFILE_NAME)))
|
126
|
+
new_newfile['version'] = version
|
127
|
+
File.open File.join(PROJECT_DIRECTORY, NEWFILE_NAME), 'w+' do |f|
|
128
|
+
f.write new_newfile.to_yaml
|
129
|
+
end
|
130
|
+
|
131
|
+
new_tasks.each do |task|
|
132
|
+
S.ay "Running `#{task.name}`", :header
|
133
|
+
task.run
|
134
|
+
end
|
135
|
+
|
136
|
+
# release summary
|
137
|
+
S.ay
|
138
|
+
S.ay 'Version ', :newline => false
|
139
|
+
S.ay "#{@@new_object[:version]}", :preset => :header, :newline => false
|
140
|
+
S.ay ' of ', :newline => false
|
141
|
+
S.ay "#{@@new_object[:name]}", :preset => :header, :newline => false
|
142
|
+
S.ay ' successfully released!'
|
143
|
+
end
|
71
144
|
end
|
data/lib/new/cli.rb
CHANGED
@@ -1,94 +1,541 @@
|
|
1
|
+
require 'active_support/core_ext/hash/reverse_merge'
|
2
|
+
require 'active_support/core_ext/hash/keys'
|
3
|
+
require 'cli_miami'
|
4
|
+
require 'listen'
|
5
|
+
require 'pp'
|
6
|
+
require 'semantic'
|
1
7
|
require 'thor'
|
2
8
|
require 'yaml'
|
3
9
|
|
10
|
+
CliMiami.set_preset :error, :color => :red
|
11
|
+
CliMiami.set_preset :fine_print, :color => :cyan
|
12
|
+
CliMiami.set_preset :header, :color => :green
|
13
|
+
CliMiami.set_preset :highlight_key, :indent => 2, :newline => false, :padding => 30, :justify => :rjust
|
14
|
+
CliMiami.set_preset :highlight_value, :color => :blue, :style => :bright, :indent => 1
|
15
|
+
CliMiami.set_preset :instruction, :color => :yellow, :indent => 2
|
16
|
+
CliMiami.set_preset :list_item, :indent => 2
|
17
|
+
CliMiami.set_preset :prompt, :color => :yellow, :style => :bold
|
18
|
+
|
19
|
+
# create a horizontal line
|
20
|
+
HR = -> {S.ay('-' * 32 , :header)}
|
21
|
+
|
4
22
|
class New::Cli < Thor
|
5
|
-
desc '
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
desc 'init', 'Create a Newfile for your project'
|
24
|
+
option :name, :type => :string, :aliases => ['-n'], :default => '', :desc => 'Your project name'
|
25
|
+
option :version, :type => :string, :aliases => ['-v'], :default => '', :desc => 'Your project\'s current version'
|
26
|
+
option :tasks, :type => :array, :aliases => ['-t'], :default => [], :desc => 'Tasks to run when releasing your project'
|
27
|
+
def init
|
28
|
+
New.load_newfiles
|
29
|
+
New::Source.load_sources
|
30
|
+
|
31
|
+
# initialize empty newfile object
|
32
|
+
newfile_object = {
|
33
|
+
:tasks => {}
|
34
|
+
}
|
35
|
+
|
36
|
+
# get valid name
|
37
|
+
name = @options['name']
|
38
|
+
until !name.empty?
|
39
|
+
name = A.sk 'Project Name:', :prompt
|
40
|
+
end
|
41
|
+
newfile_object[:name] = name
|
42
|
+
|
43
|
+
# get valid version
|
44
|
+
version = Semantic::Version.new(@options['version']) rescue nil
|
45
|
+
until !version.to_s.empty?
|
46
|
+
begin
|
47
|
+
response = A.sk 'Current Project Version:', :prompt
|
48
|
+
version = Semantic::Version.new response
|
49
|
+
rescue
|
50
|
+
S.ay "`#{response}` is not a valid semantic version (e.g. 1.2.3)", :error
|
51
|
+
end
|
52
|
+
end
|
53
|
+
newfile_object[:version] = version.to_s
|
54
|
+
|
55
|
+
# get tasks
|
56
|
+
tasks_list = []
|
57
|
+
@options['tasks'].each do |task|
|
58
|
+
tasks_list << New::Source.find_task(task)
|
59
|
+
end
|
60
|
+
|
61
|
+
# remove any empty tasks in case the user specified an invalid task
|
62
|
+
tasks_list.compact!
|
63
|
+
|
64
|
+
# if no tasks are specified, show all available tasks
|
65
|
+
if tasks_list.empty?
|
66
|
+
S.ay
|
67
|
+
self.tasks :show_source => true, :load_newfiles => false, :load_sources => false
|
68
|
+
|
69
|
+
S.ay 'Add multiple tasks by pressing ENTER after each one', :instruction
|
70
|
+
S.ay 'Enter tasks in the order you want them to run', :instruction
|
71
|
+
S.ay 'Enter both the source and the task (e.g. source#task)', :instruction
|
72
|
+
S.ay 'Enter an empty value to finish', :instruction
|
73
|
+
|
74
|
+
added_task = nil
|
75
|
+
until added_task == '' && !tasks_list.empty?
|
76
|
+
S.ay
|
77
|
+
added_task = A.sk 'Add a task:', :prompt
|
78
|
+
|
79
|
+
# if a task is entered, verify it exists
|
80
|
+
unless added_task.empty?
|
81
|
+
|
82
|
+
# find task
|
83
|
+
task = New::Source.find_task added_task
|
84
|
+
|
85
|
+
# add task to array
|
86
|
+
if task
|
87
|
+
tasks_list << task
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
S.ay
|
93
|
+
end
|
94
|
+
|
95
|
+
# output the summary so far (no task options entered yet)
|
96
|
+
padding = 10
|
97
|
+
S.ay 'Name:', :preset => :highlight_key, :padding => padding
|
98
|
+
S.ay name, :preset => :highlight_value
|
99
|
+
S.ay 'Version:', :preset => :highlight_key, :padding => padding
|
100
|
+
S.ay version.to_s, :preset => :highlight_value
|
101
|
+
S.ay 'Tasks:', :preset => :highlight_key, :padding => padding
|
102
|
+
|
103
|
+
# output first task without a padding so it looks nicer
|
104
|
+
tasks_dup = tasks_list.dup
|
105
|
+
first_task = tasks_dup.shift
|
106
|
+
S.ay "#{first_task.source.name}##{first_task.name}", :highlight_value
|
107
|
+
tasks_dup.each do |task|
|
108
|
+
S.ay "#{task.source.name}##{task.name}", :preset => :highlight_value, :indent => padding + 3
|
109
|
+
end
|
110
|
+
S.ay
|
111
|
+
|
112
|
+
tasks_list.each do |task|
|
113
|
+
HR.call
|
114
|
+
S.ay 'OK, now lets set options for', :highlight_key
|
115
|
+
S.ay task.name.to_s.upcase, :highlight_value
|
116
|
+
HR.call
|
117
|
+
S.ay
|
118
|
+
|
119
|
+
newfile_object[:tasks][task.name] = {}
|
120
|
+
task.class_options.each do |option_name, option_settings|
|
121
|
+
S.ay option_name.to_s, :preset => :highlight_key, :padding => 0
|
122
|
+
S.ay option_settings[:description], :highlight_value
|
123
|
+
|
124
|
+
# show default
|
125
|
+
default = option_settings[:default]
|
126
|
+
if default && !option_settings[:required]
|
127
|
+
default = case default
|
128
|
+
when Array then default.join(', ')
|
129
|
+
when Hash then default.keys.join(', ')
|
130
|
+
else default.to_s
|
131
|
+
end
|
132
|
+
|
133
|
+
S.ay "default: #{default}", :preset => :fine_print, :indent => option_name.length + 3
|
134
|
+
end
|
135
|
+
|
136
|
+
# GET USER INPUT FOR ARRAY TYPE
|
137
|
+
#
|
138
|
+
option_type = option_settings[:type]
|
139
|
+
case
|
140
|
+
|
141
|
+
# collect array option type values
|
142
|
+
when option_type == Array
|
143
|
+
# cast type onto all user input values (default is String)
|
144
|
+
klass = option_settings[:validation] || String
|
145
|
+
|
146
|
+
# collect array elements from the user
|
147
|
+
option_value = nil
|
148
|
+
until option_value
|
149
|
+
begin
|
150
|
+
option_value = get_array_from_user(klass)
|
151
|
+
option_value = New::Task.validate_option(option_name, option_settings, option_value)
|
152
|
+
rescue
|
153
|
+
option_value = nil
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# collect hash option type values
|
158
|
+
when option_type == Hash
|
159
|
+
# loop through the expected keys from the validation and get users input
|
160
|
+
option_value = nil
|
161
|
+
until option_value
|
162
|
+
begin
|
163
|
+
option_value = get_hash_from_user(option_settings[:validation])
|
164
|
+
option_value = New::Task.validate_option(option_name, option_settings, option_value)
|
165
|
+
rescue
|
166
|
+
option_value = nil
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# collect non array/hash option type value
|
171
|
+
else
|
172
|
+
option_value = nil
|
173
|
+
until option_value
|
174
|
+
A.sk '', :newline => false, :preset => :prompt do |response|
|
175
|
+
option_value = New::Task.validate_option(option_name, option_settings, response) rescue nil
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
newfile_object[:tasks][task.name][option_name] = option_value
|
181
|
+
S.ay
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# write project Newfile
|
186
|
+
project_newfile = File.join New::PROJECT_DIRECTORY, New::NEWFILE_NAME
|
187
|
+
File.open project_newfile, 'w+' do |f|
|
188
|
+
f.write newfile_object.deep_stringify_keys.to_yaml
|
29
189
|
end
|
190
|
+
|
191
|
+
# Success Message
|
192
|
+
S.ay "A `#{'Newfile'.green}` was successfully created for your project `#{name.to_s.green}`"
|
193
|
+
S.ay 'Open the file to verify the values are correct, and make any neccessary modifications.'
|
194
|
+
S.ay "You are now ready to run `#{'new release'.green}` to release your software into the wild!"
|
195
|
+
S.ay
|
30
196
|
end
|
31
197
|
|
32
|
-
desc '
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
198
|
+
desc 'tasks', 'List all available tasks'
|
199
|
+
option :sources, :type => :boolean, :aliases => ['-s'], :default => false, :desc => 'Show/Hide task sources'
|
200
|
+
def tasks args = {}
|
201
|
+
# merge into default options
|
202
|
+
options = {
|
203
|
+
:show_source => (@options['sources'] || false),
|
204
|
+
:load_newfiles => true,
|
205
|
+
:load_sources => true
|
206
|
+
}.merge(args)
|
207
|
+
|
208
|
+
New.load_newfiles if options[:load_newfiles]
|
209
|
+
if options[:load_sources]
|
210
|
+
S.ay
|
211
|
+
S.ay 'Fetching sources...', :header
|
212
|
+
S.ay "Use the #{'green'.green} value for defining task sources in your Newfile", :indent => 2 if options[:show_source]
|
213
|
+
S.ay
|
214
|
+
New::Source.load_sources
|
215
|
+
end
|
216
|
+
|
217
|
+
New::Source.sources.each do |source_name, source|
|
218
|
+
# determine the widest task & add some padding
|
219
|
+
longest_task_length = source.tasks.keys.map(&:length).max
|
220
|
+
|
221
|
+
S.ay source_name.to_s, :indent => 2, :newline => false, :style => :underline
|
222
|
+
S.ay source.path, :highlight_value
|
223
|
+
|
224
|
+
source.tasks.each do |task_name, task|
|
225
|
+
if options[:show_source]
|
226
|
+
padding = longest_task_length + source_name.to_s.length + 2
|
227
|
+
S.ay "#{source_name}##{task_name}", :preset => :header, :newline => false, :indent => 2, :padding => padding, :justify => :ljust
|
228
|
+
else
|
229
|
+
padding = longest_task_length + 2
|
230
|
+
S.ay task_name.to_s, :preset => :header, :newline => false, :indent => 2, :padding => padding, :justify => :ljust
|
231
|
+
end
|
232
|
+
S.ay task.description, :indent => 2
|
46
233
|
end
|
47
234
|
|
48
|
-
|
235
|
+
S.ay
|
49
236
|
end
|
50
237
|
end
|
51
238
|
|
52
|
-
desc 'release', 'Release
|
239
|
+
desc 'release', 'Release a new version of your project'
|
240
|
+
option :verbose, :type => :boolean, :aliases => ['-v'], :default => false, :desc => 'Verbose mode'
|
241
|
+
option :skip, :type => :array, :aliases => ['-s'], :default => [], :desc => 'Tasks to skip for this release'
|
53
242
|
def release
|
54
|
-
|
55
|
-
|
243
|
+
New.set_verbose if @options['verbose']
|
244
|
+
New.set_cli
|
245
|
+
New.load_newfiles
|
56
246
|
|
57
|
-
#
|
58
|
-
|
247
|
+
# request the version to bump
|
248
|
+
S.ay
|
249
|
+
S.ay 'Releasing a new version of: ', :highlight_key
|
250
|
+
S.ay New.new_object[:name], :highlight_value
|
251
|
+
S.ay 'What do you want to bump: ', :highlight_key
|
252
|
+
S.ay "[#{'Mmp'.green}] (#{'M'.green}ajor / #{'m'.green}inor / #{'p'.green}atch)", :preset => :highlight_value, :color => :white
|
253
|
+
version = Semantic::Version.new New.new_object[:version]
|
254
|
+
version_bump_part = nil
|
255
|
+
until version_bump_part
|
256
|
+
S.ay 'Current Version:', :highlight_key
|
257
|
+
A.sk version.to_s, :highlight_value do |response|
|
258
|
+
version_bump_part = case response
|
259
|
+
when 'M'
|
260
|
+
version.major += 1
|
261
|
+
version.minor = 0
|
262
|
+
version.patch = 0
|
263
|
+
when 'm'
|
264
|
+
version.minor += 1
|
265
|
+
version.patch = 0
|
266
|
+
when 'p'
|
267
|
+
version.patch += 1
|
268
|
+
else
|
269
|
+
S.ay 'You must choose from [Mmp]', :error
|
270
|
+
nil
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
S.ay 'New Version: ', :highlight_key
|
275
|
+
S.ay version.to_s, :highlight_value
|
276
|
+
S.ay
|
59
277
|
|
60
|
-
#
|
61
|
-
|
278
|
+
# collect a list of changes in this version
|
279
|
+
changelog = get_changelog_from_user
|
280
|
+
S.ay
|
62
281
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
282
|
+
# show tasks
|
283
|
+
S.ay "#{'Running Tasks:'.green} (in order)"
|
284
|
+
skip_tasks = @options['skip'].map(&:to_sym)
|
285
|
+
New.new_object[:tasks].keys.each do |task|
|
286
|
+
if skip_tasks.include?(task)
|
287
|
+
S.ay "#{task.to_s} (skipped)", :preset => :fine_print, :indent => 4
|
288
|
+
else
|
289
|
+
S.ay task.to_s, :indent => 2
|
290
|
+
end
|
291
|
+
end
|
292
|
+
S.ay
|
293
|
+
|
294
|
+
New.new version.to_s, changelog, @options['skip']
|
295
|
+
end
|
296
|
+
|
297
|
+
desc 'version', 'Show the current version'
|
298
|
+
def version
|
299
|
+
New.load_newfiles
|
300
|
+
S.ay New.new_object[:name], :highlight_key
|
301
|
+
S.ay New.new_object[:version], :highlight_value
|
302
|
+
end
|
303
|
+
|
304
|
+
desc 'test', 'Run task tests from sources'
|
305
|
+
option :watch, :type => :boolean, :aliases => ['-w'], :desc => 'Watch local tasks for changes and run tests'
|
306
|
+
option :source, :type => :string, :aliases => ['-s'], :desc => 'Source name'
|
307
|
+
option :task, :type => :string, :aliases => ['-t'], :desc => 'Task name'
|
308
|
+
def test
|
309
|
+
spec_paths = []
|
310
|
+
watch_dirs = []
|
311
|
+
|
312
|
+
New.load_newfiles
|
313
|
+
New::Source.load_sources
|
314
|
+
|
315
|
+
# create a hash with a single source if one is passed
|
316
|
+
sources = if @options['source']
|
317
|
+
source_name = @options['source'].to_sym
|
318
|
+
source_hash = {}
|
319
|
+
source_hash[source_name] = New::Source.sources[source_name]
|
320
|
+
source_hash
|
321
|
+
else
|
322
|
+
New::Source.sources
|
323
|
+
end
|
324
|
+
|
325
|
+
# collect specs to run/watch
|
326
|
+
sources.each do |source_name, source|
|
327
|
+
next unless source
|
328
|
+
|
329
|
+
# create a hash with a single task if one is passed
|
330
|
+
tasks = @options['task'] ? [source.tasks[options['task'].to_sym]] : source.tasks
|
331
|
+
tasks = if @options['task']
|
332
|
+
task_name = @options['task'].to_sym
|
333
|
+
task_hash = {}
|
334
|
+
task_hash[task_name] = source.tasks[task_name]
|
335
|
+
task_hash
|
336
|
+
else
|
337
|
+
source.tasks
|
338
|
+
end
|
339
|
+
|
340
|
+
tasks.each do |task_name, task|
|
341
|
+
next unless task.path
|
342
|
+
|
343
|
+
spec_path = File.join(File.dirname(task.path), "#{task_name}_task_spec.rb")
|
344
|
+
|
345
|
+
# if the source/task has a spec file, and the source is local, watch the task directory for changes and run the spec whenever anything changes
|
346
|
+
if File.file?(spec_path) && File.directory?(source.path)
|
347
|
+
# find task directory in original path, not the sourcerer tmp directory
|
348
|
+
original_task_dir_path = File.dirname(Dir[File.join(source.path, '**', File.basename(task.path))][0])
|
349
|
+
|
350
|
+
watch_dirs << original_task_dir_path
|
351
|
+
spec_paths << spec_path
|
70
352
|
end
|
71
|
-
rescue LoadError
|
72
|
-
New.say "No task '#{task}' found!", type: :fail
|
73
|
-
next
|
74
353
|
end
|
75
|
-
|
76
|
-
|
354
|
+
end
|
355
|
+
|
356
|
+
# run tests
|
357
|
+
if !spec_paths.empty?
|
358
|
+
# S.ay "Running tests for `#{task_name}` task in `#{source_name}` source...", :warn
|
359
|
+
Kernel::system "bundle exec rspec #{spec_paths.join(' ')}"
|
360
|
+
end
|
361
|
+
|
362
|
+
# watch tests
|
363
|
+
# if watch files are found, start a listener to run the spec
|
364
|
+
if @options['watch'] && !watch_dirs.empty?
|
365
|
+
listener = Listen.to *watch_dirs do |modified, added, removed|
|
366
|
+
all = modified + added + removed
|
367
|
+
|
368
|
+
# find sibling spec file from modified file
|
369
|
+
spec_path = all.collect{ |file| Dir[File.join(File.dirname(file), '*_spec.rb')] }.flatten.first
|
370
|
+
|
371
|
+
Kernel::system "bundle exec rspec #{spec_path}"
|
372
|
+
end
|
373
|
+
listener.start
|
374
|
+
Kernel::sleep
|
77
375
|
end
|
78
376
|
end
|
79
377
|
|
80
|
-
|
378
|
+
no_commands do
|
379
|
+
def get_changelog_from_user
|
380
|
+
S.ay 'Lets add some items to the changelog', :header
|
381
|
+
S.ay 'Add multiple entries by pressing ENTER after each one', :instruction
|
382
|
+
S.ay 'Enter an empty value to finish', :instruction
|
81
383
|
|
82
|
-
|
83
|
-
|
84
|
-
New.say "A project named #{name} already exists...", type: :warn
|
85
|
-
New.say " Overwrite it? [Yn]", type: :warn
|
86
|
-
exit if STDIN.gets.chomp! != 'Y'
|
384
|
+
user_changelog = []
|
385
|
+
user_response = nil
|
87
386
|
|
88
|
-
#
|
89
|
-
|
387
|
+
# add entries to the changelog until an empty string is entered
|
388
|
+
until user_response == '' && !user_changelog.empty?
|
389
|
+
A.sk user_changelog.compact.join("\n"), :prompt do |response|
|
390
|
+
if response.empty?
|
391
|
+
user_response = ''
|
392
|
+
next
|
393
|
+
end
|
394
|
+
|
395
|
+
user_changelog << user_response = response
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
user_changelog
|
90
400
|
end
|
91
401
|
|
92
|
-
|
402
|
+
def get_array_from_user validation = String
|
403
|
+
# start to build the array of user values
|
404
|
+
user_array = []
|
405
|
+
|
406
|
+
# convert array to hash of Strings
|
407
|
+
if validation.is_a? Array
|
408
|
+
validation_hash = {}
|
409
|
+
validation.each do |v|
|
410
|
+
validation_hash[v.to_sym] = String
|
411
|
+
end
|
412
|
+
validation = validation_hash
|
413
|
+
end
|
414
|
+
|
415
|
+
if validation.is_a? Hash
|
416
|
+
S.ay 'We need to collect a list of complex objects', :header
|
417
|
+
|
418
|
+
user_response = nil
|
419
|
+
until user_response == 'n'
|
420
|
+
S.ay "#{user_array.length} objects created: ", :indent => 2, :newline => false
|
421
|
+
S.ay user_array.join(', '), :highlight_value
|
422
|
+
A.sk 'Press ENTER to create another object. Enter `n` to stop.', :prompt do |response|
|
423
|
+
if response == 'n'
|
424
|
+
user_response = 'n'
|
425
|
+
next
|
426
|
+
end
|
427
|
+
|
428
|
+
user_response = get_hash_from_user(validation, false)
|
429
|
+
user_array << user_response
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
else
|
434
|
+
S.ay "We need to collect a list of #{validation}s", :header
|
435
|
+
S.ay 'Add multiple values by pressing ENTER after each one', :instruction
|
436
|
+
S.ay 'Enter an empty value to finish', :instruction
|
437
|
+
|
438
|
+
# add to the array until an empty string is entered
|
439
|
+
user_response = nil
|
440
|
+
until user_response == ''
|
441
|
+
A.sk user_array.compact.join(', '), :prompt do |response|
|
442
|
+
|
443
|
+
# if the option is valid, pass through the empty string to end entering values
|
444
|
+
if response.empty?
|
445
|
+
user_response = ''
|
446
|
+
next
|
447
|
+
end
|
448
|
+
|
449
|
+
user_response = New::Task.validate_class(response, validation) rescue nil
|
450
|
+
user_array << user_response
|
451
|
+
end
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
return user_array.compact
|
456
|
+
end
|
457
|
+
|
458
|
+
def get_hash_from_user validation = {}, allow_custom_keys = true
|
459
|
+
# start to build the hash of user values
|
460
|
+
user_hash = {}
|
461
|
+
|
462
|
+
# convert array to hash of Strings
|
463
|
+
if validation.is_a? Array
|
464
|
+
validation_hash = {}
|
465
|
+
validation.each do |v|
|
466
|
+
validation_hash[v.to_sym] = String
|
467
|
+
end
|
468
|
+
validation = validation_hash
|
469
|
+
end
|
470
|
+
|
471
|
+
# get user values for required validation keys
|
472
|
+
validation.each do |key, klass|
|
473
|
+
S.ay 'We need to collect some required values', :header
|
474
|
+
|
475
|
+
user_response = nil
|
476
|
+
until user_response
|
477
|
+
|
478
|
+
# do not allow nested arrays/hashes
|
479
|
+
# these should be declared as their own option
|
480
|
+
if klass == Array || klass == Hash
|
481
|
+
S.ay 'Hash options cannot have nested Arrays or Hashes. They should be declared as their own option.', :error
|
482
|
+
exit
|
483
|
+
end
|
484
|
+
|
485
|
+
A.sk "Enter a VALUE for `#{key}`", :prompt do |response|
|
486
|
+
# make sure validation keys have a value
|
487
|
+
if response == ''
|
488
|
+
user_response = nil
|
489
|
+
next
|
490
|
+
end
|
491
|
+
|
492
|
+
begin
|
493
|
+
user_response = New::Task.validate_class(response, klass)
|
494
|
+
user_hash[key] = user_response
|
495
|
+
S.ay user_hash
|
496
|
+
rescue
|
497
|
+
user_response = nil
|
498
|
+
end
|
499
|
+
end
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
if allow_custom_keys
|
504
|
+
# Allow users to enter custom keys AND values
|
505
|
+
S.ay 'You can add custom keys & values if you want', :header
|
506
|
+
S.ay 'Add multiple key/value pairs by pressing ENTER after each one', :instruction
|
507
|
+
S.ay 'Enter an empty key to finish', :instruction
|
508
|
+
|
509
|
+
user_key_response = nil
|
510
|
+
until user_key_response == ''
|
511
|
+
A.sk 'Enter a KEY name', :prompt do |key_response|
|
512
|
+
# exit loop if user is done entering info
|
513
|
+
if key_response == ''
|
514
|
+
user_key_response = ''
|
515
|
+
next
|
516
|
+
end
|
517
|
+
|
518
|
+
user_value_response = nil
|
519
|
+
until user_value_response
|
520
|
+
A.sk "Enter a VALUE for `#{key_response}`", :prompt do |value_response|
|
521
|
+
# make sure value exists for user created key
|
522
|
+
if value_response == ''
|
523
|
+
user_value_response = nil
|
524
|
+
next
|
525
|
+
end
|
526
|
+
|
527
|
+
# create key/value pair
|
528
|
+
user_hash[key_response.to_sym] = user_value_response = value_response
|
529
|
+
S.ay user_hash
|
530
|
+
end
|
531
|
+
end
|
532
|
+
end
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
return user_hash
|
537
|
+
end
|
93
538
|
end
|
539
|
+
|
540
|
+
default_task :release
|
94
541
|
end
|