brightpearl-cli 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/core/jira.rb ADDED
@@ -0,0 +1,43 @@
1
+ module Brightpearl
2
+
3
+ class Jira
4
+
5
+ # Initialise credentials
6
+ # @return void
7
+ def initialize
8
+ end
9
+
10
+ # Gets jira number from an argument. Displays error if invalid.
11
+ # @return int
12
+ def get_jira_number_from_arg(argument)
13
+ jira_number = nil
14
+ jira_number_possible = argument
15
+ jira_number_possible.split('-').each do |pjn|
16
+ if validate_jira_number(pjn)
17
+ jira_number = pjn
18
+ break
19
+ end
20
+ end
21
+ if jira_number.nil?
22
+ Brightpearl::Terminal::error('Invalid Jira Number', "The Jira Number must be a 4-5 digit number. You tried to pass: #{Brightpearl::Terminal::format_invalid(argument)}", true)
23
+ end
24
+ jira_number
25
+ end
26
+
27
+ # Validates that string is a number between 1 - 20,0000
28
+ # @return boolean
29
+ def validate_jira_number(value)
30
+ begin
31
+ value = Integer value
32
+ if value < 0 || value > 20000
33
+ return false
34
+ end
35
+ return true
36
+ rescue
37
+ return false
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ end
data/lib/core/mysql.rb ADDED
@@ -0,0 +1,48 @@
1
+ require 'mysql'
2
+
3
+ module Brightpearl
4
+
5
+ class MySQL
6
+
7
+ DEFAULT_SCHEMA = 'app'
8
+
9
+ @database_connection = nil
10
+
11
+ @ids_customers = []
12
+ @ids_shipping = []
13
+
14
+ def self.get_database_connection(schema = DEFAULT_SCHEMA)
15
+ if @database_connection == nil
16
+ @database_connection = Mysql.new(
17
+ Brightpearl::Config.param(Brightpearl::Config::VM_IP),
18
+ Brightpearl::Config.param(Brightpearl::Config::VM_MYSQL_USER),
19
+ Brightpearl::Config.param(Brightpearl::Config::VM_MYSQL_PASSWORD),
20
+ schema
21
+ )
22
+ end
23
+ @database_connection
24
+ end
25
+
26
+ def self.get_random_customer_id
27
+ unless @ids_customers.any?
28
+ customers = Brightpearl::MySQL::get_database_connection.query('SELECT customers_id FROM customers ORDER BY customers_id DESC LIMIT 100')
29
+ customers.each_hash do |row|
30
+ @ids_customers << row['customers_id']
31
+ end
32
+ end
33
+ @ids_customers.sample
34
+ end
35
+
36
+ def self.get_random_shipping_id
37
+ unless @ids_shipping.any?
38
+ ship_methods = Brightpearl::MySQL::get_database_connection.query('SELECT ship_method_id FROM ship_methods ORDER BY ship_method_id DESC LIMIT 100')
39
+ ship_methods.each_hash do |row|
40
+ @ids_shipping << row['ship_method_id']
41
+ end
42
+ end
43
+ @ids_shipping.sample
44
+ end
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,389 @@
1
+ require 'highline/import'
2
+ require 'columnist'
3
+
4
+ module Brightpearl
5
+
6
+ class Terminal
7
+
8
+ extend Columnist
9
+
10
+ MSG_INFO = 'info'
11
+ MSG_WARNING = 'warning'
12
+ MSG_ERROR = 'error'
13
+
14
+ # Runs a series of commands in the terminal.
15
+ # @return void
16
+ def self.command(commands, location = nil, verbose = true, verbose_cd = true, capture_real_output = false)
17
+ unless commands.is_a?(Array)
18
+ commands = [commands]
19
+ end
20
+ unless location.nil?
21
+ unless File.directory?(location)
22
+ error('Directory not found.', "Cannot find the following directory: \x1B[38;5;205m#{location}\x1B[0m", true)
23
+ end
24
+ end
25
+ output = []
26
+ if verbose_cd && verbose && commands.any?
27
+ puts "\x1B[42m Executing \x1B[0m \x1B[32m\xe2\x86\x92\x1B[0m #{Brightpearl::Terminal::format_command("cd #{location}")}"
28
+ end
29
+ if commands.any?
30
+ commands.each do |command|
31
+ if verbose
32
+ puts "\x1B[42m Executing \x1B[0m \x1B[32m\xe2\x86\x92\x1B[0m #{Brightpearl::Terminal::format_command("#{command}")}"
33
+ end
34
+ if location.nil?
35
+ if capture_real_output
36
+ output << `#{command}`
37
+ else
38
+ output << system("#{command}")
39
+ end
40
+ else
41
+ if capture_real_output
42
+ output << `cd #{location} && #{command}`
43
+ else
44
+ output << system("cd #{location} && #{command}")
45
+ end
46
+ end
47
+ end
48
+ end
49
+ if capture_real_output
50
+ output.map! { |v| v.gsub('\n', "\n") }
51
+ end
52
+ output
53
+ end
54
+
55
+ # Runs a series of commands in the terminal (and captures the output).
56
+ # @return void
57
+ def self.command_capture(commands, location = nil, verbose = true, verbose_cd = true)
58
+ command(commands, location, verbose, verbose_cd, true)
59
+ end
60
+
61
+ # Outputs messages to the terminal.
62
+ # @return void
63
+ def self.output(message = 'No message', type = MSG_INFO)
64
+ case type
65
+ when MSG_INFO
66
+ puts "\x1B[48;5;2m Executing \x1B[0m \x1B[32m\xe2\x86\x92\x1B[0m \x1B[0m#{message}\x1B[0m"
67
+ when MSG_WARNING
68
+ puts "\x1B[48;5;2m Vigilance \x1B[0m \x1B[33m\xe2\x86\x92\x1B[0m \x1B[0m#{message}\x1B[0m"
69
+ when MSG_ERROR
70
+ puts " \x1B[48;5;2m Failure \x1B[0m \x1B[33m\xe2\x86\x92\x1B[0m \x1B[0m#{message}\x1B[0m"
71
+ else
72
+ raise RuntimeError "'#{type}' is not a valid type."
73
+ end
74
+ end
75
+
76
+ # Displays error and exits script by default.
77
+ # @return void
78
+ def self.abort(title = nil, message = nil, exit_script = true, preceding_blank_line = true)
79
+ if title.nil?
80
+ title = 'Abandon ship!'
81
+ end
82
+ if message.nil?
83
+ message = "You have chosen to \x1B[38;5;9mABORT\x1B[38;5;240m the script."
84
+ end
85
+ Brightpearl::Terminal::error(title, message, exit_script, preceding_blank_line, 'ABORT')
86
+ end
87
+
88
+ # Displays error and exits script by default.
89
+ # @return void
90
+ def self.error(title = nil, message = nil, exit_script = true, preceding_blank_line = true, error_text = 'ERROR')
91
+ if title.nil?
92
+ title = "It seems you're trying to do something silly."
93
+ end
94
+ if preceding_blank_line
95
+ puts
96
+ end
97
+ puts " \x1B[48;5;196m #{error_text.upcase} \x1B[0m \xe2\x86\x92 #{title}"
98
+ puts
99
+ unless message.nil?
100
+ if message.is_a?(Array)
101
+ messages = message
102
+ else
103
+ messages = message.split("\n")
104
+ end
105
+ table(:border => false) do
106
+ messages.each do |line|
107
+ row do
108
+ column('', :align => 'left', :width => 4)
109
+ column("\x1B[38;5;240m#{line}\x1B[0m", :align => 'left', :width => 180)
110
+ end
111
+ end
112
+ end
113
+ puts
114
+ end
115
+ if exit_script
116
+ exit
117
+ end
118
+ end
119
+
120
+ # Displays info message.
121
+ # @return void
122
+ def self.info(title = nil, message = nil, preceding_blank_line = true)
123
+ if title.nil?
124
+ title = 'Whatever you did, worked.'
125
+ end
126
+ if preceding_blank_line
127
+ puts
128
+ end
129
+ puts " \x1B[48;5;27m MESSAGE \x1B[0m \xe2\x86\x92 #{title}"
130
+ puts
131
+ unless message.nil?
132
+ if message.is_a?(Array)
133
+ messages = message
134
+ else
135
+ messages = message.split("\n")
136
+ end
137
+ table(:border => false) do
138
+ messages.each do |line|
139
+ row do
140
+ column('', :align => 'left', :width => 4)
141
+ column("\x1B[38;5;240m#{line}\x1B[0m", :align => 'left', :width => 180)
142
+ end
143
+ end
144
+ end
145
+ puts
146
+ end
147
+ end
148
+
149
+ # Displays success message.
150
+ # @return void
151
+ def self.success(title = nil, message = nil, preceding_blank_line = true)
152
+ if title.nil?
153
+ title = 'Whatever you did, worked.'
154
+ end
155
+ if preceding_blank_line
156
+ puts
157
+ end
158
+ puts " \x1B[48;5;34m SUCCESS \x1B[0m \xe2\x86\x92 #{title}"
159
+ puts
160
+ unless message.nil?
161
+ if message.is_a?(Array)
162
+ messages = message
163
+ else
164
+ messages = message.split("\n")
165
+ end
166
+ table(:border => false) do
167
+ messages.each do |line|
168
+ row do
169
+ column('', :align => 'left', :width => 4)
170
+ column("\x1B[38;5;240m#{line}\x1B[0m", :align => 'left', :width => 180)
171
+ end
172
+ end
173
+ end
174
+ puts
175
+ end
176
+ end
177
+
178
+ # Displays warning message.
179
+ # @return void
180
+ def self.warning(title = nil, message = nil, preceding_blank_line = true)
181
+ if title.nil?
182
+ title = 'Whatever you did, generated a warning.'
183
+ end
184
+ if preceding_blank_line
185
+ puts
186
+ end
187
+ puts " \x1B[48;5;202m WARNING \x1B[0m \xe2\x86\x92 #{title}"
188
+ puts
189
+ unless message.nil?
190
+ if message.is_a?(Array)
191
+ messages = message
192
+ else
193
+ messages = message.split("\n")
194
+ end
195
+ table(:border => false) do
196
+ messages.each do |line|
197
+ row do
198
+ column('', :align => 'left', :width => 4)
199
+ column("\x1B[38;5;240m#{line}\x1B[0m", :align => 'left', :width => 180)
200
+ end
201
+ end
202
+ end
203
+ puts
204
+ end
205
+ end
206
+
207
+ # Returns action text in consistent, uniform manner.
208
+ # @return String
209
+ def self.format_action(action_text)
210
+ "\x1B[38;5;170m#{action_text.upcase}\x1B[0m"
211
+ end
212
+
213
+ # Returns branch name in consistent, uniform manner.
214
+ # @return String
215
+ def self.format_branch(branch_name)
216
+ "\x1B[38;5;40m[#{branch_name}]\x1B[0m"
217
+ end
218
+
219
+ # Returns branch name in consistent, uniform manner.
220
+ # @return String
221
+ def self.format_command(command_name)
222
+ "\x1B[38;5;220m#{command_name}\x1B[0m"
223
+ end
224
+
225
+ # Returns directory name in consistent, uniform manner.
226
+ # @return String
227
+ def self.format_directory(directory_name)
228
+ "\x1B[38;5;48m#{directory_name}\x1B[0m"
229
+ end
230
+
231
+ # Returns branch name in consistent, uniform manner.
232
+ # @return String
233
+ def self.format_flag(flag_letter)
234
+ "\x1B[38;5;117m-#{flag_letter} flag\x1B[0m"
235
+ end
236
+
237
+ # Returns branch name in consistent, uniform manner.
238
+ # @return String
239
+ def self.format_invalid(invalid_string)
240
+ "\x1B[38;5;9m#{invalid_string}\x1B[0m"
241
+ end
242
+
243
+ # Generates 'filler' string.
244
+ # @return String
245
+ def self.fill(length = 0, string = ' ')
246
+ fill_string = ''
247
+ (0..length - 1).each {
248
+ fill_string = "#{fill_string}#{string}"
249
+ }
250
+ fill_string
251
+ end
252
+
253
+ # Gives a prompt where 'y/Y' will return TRUE, 'n/N' will return false, and ANY other key will do nothing.
254
+ # @return void
255
+ def self.prompt_yes_no(title = nil, message = nil, confirmation_message = nil, preceding_blank_line = true)
256
+ if title.nil?
257
+ title = 'Please confirm YES or NO.'
258
+ end
259
+ if confirmation_message.nil?
260
+ confirmation_message = 'Would you like to continue?';
261
+ end
262
+ if preceding_blank_line
263
+ puts
264
+ end
265
+ puts " \x1B[48;5;56m CONFIRM \x1B[0m \xe2\x86\x92 #{title}"
266
+ puts
267
+ unless message.nil?
268
+ if message.is_a?(Array)
269
+ messages = message
270
+ else
271
+ messages = message.split("\n")
272
+ end
273
+ table(:border => false) do
274
+ messages.each do |line|
275
+ row do
276
+ column('', :align => 'left', :width => 4)
277
+ column("\x1B[38;5;240m#{line}\x1B[0m", :align => 'left', :width => 180)
278
+ end
279
+ end
280
+ end
281
+ puts
282
+ end
283
+ response = ''
284
+ until %w[y Y n N x X a A].include? response
285
+ response = ask(" \x1B[38;5;89m#{confirmation_message} \x1B[0m[y/n]\x1B[90m => \x1B[0m")
286
+ end
287
+ case response.downcase
288
+ when 'y'
289
+ puts "\n"
290
+ return true
291
+ when 'n'
292
+ puts "\n"
293
+ return false
294
+ when 'a'
295
+ Brightpearl::Terminal::error('Abandon ship!', ["You have chosen to \x1B[38;5;9mABORT\x1B[38;5;240m the script.", nil, 'Please note that whenever you do this, any scripted tasks which were running', 'will have been interrupted mid-script. This may (or may not) cause problems.'], true)
296
+ when 'x'
297
+ Brightpearl::Terminal::error('Abandon ship!', ["You have chosen to \x1B[38;5;9mABORT\x1B[38;5;240m the script.", nil, 'Please note that whenever you do this, any scripted tasks which were running', 'will have been interrupted mid-script. This may (or may not) cause problems.'], true)
298
+ else
299
+ end
300
+ end
301
+
302
+ # Used to display what is about to happen. Prompts for ENTER key before continuing.
303
+ # @return void
304
+ def self.prompt_enter(text)
305
+ puts
306
+ if text.is_a?(Array)
307
+ text.each do |line|
308
+ puts line
309
+ end
310
+ else
311
+ puts text
312
+ end
313
+ enter_to_continue
314
+ end
315
+
316
+ # Used to confirm what is about to happen. Prompts for [y/n] key before continuing.
317
+ # @return void
318
+ def self.prompt_yes(text)
319
+ puts
320
+ if text.is_a?(Array)
321
+ text.each do |line|
322
+ puts line
323
+ end
324
+ else
325
+ puts text
326
+ end
327
+ yes_to_continue
328
+ end
329
+
330
+ # Shows a prompt waiting for user input.
331
+ # @return string
332
+ def self.prompt_for_input(text)
333
+ response = Readline.readline(text, true)
334
+ exit if %w(quit exit q x).include?(response)
335
+ response
336
+ end
337
+
338
+ # Gives a prompt where ANY KEY will continue executing the script.
339
+ # @return void
340
+ # @deprecated
341
+ def self.any_key_to_continue(text = "\n\x1B[90mPress any key to continue\x1B[0m => ")
342
+ STDOUT.flush
343
+ print "#{text}"
344
+ STDIN.gets
345
+ nil
346
+ end
347
+
348
+ # Gives a prompt where ONLY 'Enter' will continue the script, any other key will abort.
349
+ # @return void
350
+ # @deprecated
351
+ def self.enter_to_continue
352
+ print "\n\x1B[90mPress \x1B[32mENTER\x1B[90m to continue, any other key to abort\x1B[0m => "
353
+ begin
354
+ system('stty raw -echo')
355
+ response = STDIN.getc
356
+ ensure
357
+ system('stty -raw echo')
358
+ end
359
+ if response.chr == "\r"
360
+ puts "\n\n"
361
+ else
362
+ abort("\nScript aborted.\n\n")
363
+ end
364
+ end
365
+
366
+ # Gives a prompt where ONLY 'Y' or 'y' will continue the script, any other key will abort.
367
+ # @return void
368
+ # @deprecated
369
+ def self.yes_to_continue
370
+ puts "\n"
371
+ response = ''
372
+ until %w[y n].include? response
373
+ response = ask("\x1B[90mWould you like to continue? \x1B[0m[y/n]\x1B[90m => \x1B[0m") { |q| q.limit = 1; q.case = :downcase }
374
+ end
375
+ case response
376
+ when 'y'
377
+ puts
378
+ return
379
+ else
380
+ puts
381
+ output('Aborting script', MSG_ERROR)
382
+ puts
383
+ exit
384
+ end
385
+ end
386
+
387
+ end
388
+
389
+ end
data/lib/core/tools.rb ADDED
@@ -0,0 +1,78 @@
1
+ require 'open-uri'
2
+
3
+ module Brightpearl
4
+
5
+ class Tools
6
+
7
+ REPORT_WIDTH = 188
8
+
9
+ # Check for internet access. Dies if not connected.
10
+ # @return boolean|void
11
+ def self.verify_internet_access
12
+ begin
13
+ return true if open('http://www.google.com')
14
+ rescue
15
+ abort('No internet connection. You must be online to run this command.')
16
+ end
17
+ end
18
+
19
+ # Returns time ago in human readable format.
20
+ # @string
21
+ def self.time_passed_since(time_stamp)
22
+ unless time_stamp.is_a?(DateTime)
23
+ raise RuntimeError, "Expected DateTime, got: #{time_stamp.class}"
24
+ end
25
+ seconds_ago = seconds_since(time_stamp.strftime('%Y-%m-%dT%H:%M:%S%z'))
26
+ seconds_ago = seconds_ago.to_i
27
+ case seconds_ago
28
+ when 1..119
29
+ return 'A minute ago'
30
+ when 120..2999
31
+ return "#{(seconds_ago / 60).round} minutes ago"
32
+ when 3000..5399
33
+ return 'About an hour ago'
34
+ when 5399..86399
35
+ return "#{((seconds_ago / 60) / 60).round} hours ago"
36
+ when 86400..169999
37
+ return 'A day ago'
38
+ when 170000..2505599
39
+ return "#{(((seconds_ago / 24) / 60) / 60).round} days ago"
40
+ when 2506000..4000000
41
+ return 'A month ago'
42
+ when 4000001..31535999
43
+ return "#{((((seconds_ago / 30.4368) / 24) / 60) / 60).round} months ago"
44
+ when 31536000..47303999
45
+ return 'A year ago'
46
+ when 47304000..9999999999999
47
+ return "#{((((seconds_ago / 365) / 24) / 60) / 60).round} years ago"
48
+ else
49
+ return "Out of range (#{seconds_ago})"
50
+ end
51
+ end
52
+
53
+ # Returns the number of seconds that have elapsed between a timestamp (0000-00-00T00:00:00+00:00) and now
54
+ # @return integer
55
+ def self.seconds_since(time_stamp)
56
+ time_stamp = DateTime.strptime(time_stamp, '%Y-%m-%dT%H:%M:%S%z')
57
+ time_now = DateTime.now
58
+ ((time_now - time_stamp) * 24 * 60 * 60).to_i
59
+ end
60
+
61
+ # Used to make sure all reports are the same width (for consistency).
62
+ # @return void
63
+ def self.validate_report_width(array)
64
+ unless array.is_a?(Array)
65
+ raise RuntimeError, "Expected Array, got: #{array.class}"
66
+ end
67
+ report_width = 0
68
+ array.each do |pixels|
69
+ report_width = report_width + pixels.to_i
70
+ end
71
+ unless report_width == REPORT_WIDTH
72
+ raise RuntimeError, "Report needs to be #{REPORT_WIDTH} pixels wide, calculated #{report_width} pixels."
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ end
@@ -0,0 +1,7 @@
1
+ module Brightpearl
2
+
3
+ class Validate
4
+
5
+ end
6
+
7
+ end
@@ -0,0 +1,83 @@
1
+ module BrightpearlCommand
2
+
3
+ class Build < ::Convoy::ActionCommand::Base
4
+
5
+ def execute
6
+
7
+ @opts = command_options
8
+ @args = arguments
9
+ @service_dir = nil
10
+ opts_validate
11
+ opts_routing
12
+
13
+ end
14
+
15
+ def opts_validate
16
+
17
+ if @args[0].nil?
18
+ Brightpearl::Terminal::error('Must specify service name', "The script cannot build your service because you haven't told it what service to build.", true)
19
+ end
20
+
21
+ service_dir = Brightpearl::Config.param(Brightpearl::Config::WORKSTATION_PATH_TO_BP_CODE)
22
+ dir_dev_support = "#{service_dir}/services/dev-support/#{@args[0]}/#{@args[0]}-service"
23
+ dir_functional = "#{service_dir}/services/functional/#{@args[0]}/#{@args[0]}-service"
24
+ dir_infrastructure = "#{service_dir}/services/infrastructure/#{@args[0]}/#{@args[0]}-service"
25
+ dir_integration = "#{service_dir}/services/integration/#{@args[0]}/#{@args[0]}-service"
26
+ dir_lib = "#{service_dir}/services/lib/#{@args[0]}"
27
+
28
+ if File.directory?(dir_dev_support)
29
+ @service_dir = dir_dev_support
30
+ end
31
+ if File.directory?(dir_functional)
32
+ @service_dir = dir_functional
33
+ end
34
+ if File.directory?(dir_infrastructure)
35
+ @service_dir = dir_infrastructure
36
+ end
37
+ if File.directory?(dir_integration)
38
+ @service_dir = dir_integration
39
+ end
40
+ if File.directory?(dir_lib)
41
+ @service_dir = dir_lib
42
+ end
43
+ if @service_dir.nil?
44
+ Brightpearl::Terminal::error('Cannot find service', "The script is unable to find a service called: \x1B[38;5;202m#{@args[0].downcase.capitalize}\x1B[0m\nPlease check your spelling and try again.")
45
+ end
46
+
47
+ end
48
+
49
+ def opts_routing
50
+
51
+ build_service
52
+
53
+ end
54
+
55
+ def build_service
56
+
57
+ commands = [
58
+ # 'git status'
59
+ 'mvn clean install | grep "Building war"',
60
+ # 'mvn deploy'
61
+ ]
62
+ output = Brightpearl::Terminal::command_capture(commands, @service_dir)
63
+
64
+ puts output
65
+
66
+ war_file = output[0].split('war: ')
67
+ war_file = war_file[1].chomp
68
+
69
+ puts war_file
70
+
71
+ if Brightpearl::Config.param(Brightpearl::Config::WORKSTATION_OS) == Brightpearl::Config::MAC
72
+ commands = [
73
+ "scp #{war_file} #{Brightpearl::Config.param(Brightpearl::Config::VM_USER)}@#{Brightpearl::Config.param(Brightpearl::Config::VM_IP)}:/tmp"
74
+ ]
75
+ puts commands
76
+ Brightpearl::Terminal::command(commands)
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+
83
+ end