blufin-lib 1.5.0 → 1.5.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 781d11b53064e1a5a3dea7d33e7a9d991dd0b7ee
4
- data.tar.gz: 4ba21e3918b587754588ca52f4a457885597c68c
3
+ metadata.gz: 270bafc81b6eadf414cfadf2468ad3b85bf569b8
4
+ data.tar.gz: ef34b1af83e74ec125af126924d5772d8cadf6ca
5
5
  SHA512:
6
- metadata.gz: 0c0cd453d915027c66fc137f0346ed9d19fccfc72de22ce75e6535b1edefc929f16ee2c6af11571d27219f043c6af1c0c7fa5d3d82c2216c61818b626e1735b8
7
- data.tar.gz: 49d99beb71dedc4a3b8f09bf792f4b1e1fcd552a2598499d60eafc394420b59af100759c44a35612064c4d4dde490e6d73fc83c7b546f149bc10bb6d020d95f3
6
+ metadata.gz: d95f777b6152f9b00ad9bd5ee0ac5894abd2a12f129927519656fd295f5af64d1681ff3e39d2c4d1f4c68bc9561def591c8fa198f7aae8688a4e31b5d482718d
7
+ data.tar.gz: 41b89f6e3318cc01fee5dca3b1d657fa199c39cb34f32e23446f89e812e7c55d7028874adf9d1ba446b5d0fd37d465c75e160b60db94735a8e559fb0c2449f03
@@ -1,18 +1,21 @@
1
1
  module Blufin
2
2
 
3
3
  autoload :Arrays, 'core/arrays'
4
+ autoload :Base, 'core/base'
4
5
  autoload :Browser, 'core/browser'
6
+ autoload :Config, 'core/config'
5
7
  autoload :DateTimeUtils, 'core/datetime_utils'
6
8
  autoload :Encryptor, 'core/encryptor'
7
9
  autoload :Files, 'core/files'
8
10
  autoload :Image, 'core/image'
9
11
  autoload :Network, 'core/network'
10
12
  autoload :Numbers, 'core/numbers'
13
+ autoload :Projects, 'core/projects'
11
14
  autoload :Routes, 'core/routes'
12
- autoload :SSH, 'core/ssh'
13
15
  autoload :Strings, 'core/strings'
14
16
  autoload :Terminal, 'core/terminal'
15
17
  autoload :Tools, 'core/tools'
16
18
  autoload :Validate, 'core/validate'
19
+ autoload :Versioning, 'core/versioning'
17
20
 
18
21
  end
@@ -0,0 +1,17 @@
1
+ module Blufin
2
+
3
+ class Base
4
+
5
+ OPT_PATH = '/opt'
6
+
7
+ # Get PATH to opt files.
8
+ # @return String
9
+ def self.get_base_path
10
+ base_path = File.dirname(File.expand_path(__FILE__))
11
+ base_path = base_path.gsub(/\/\w+\/\w+\z/i, '')
12
+ base_path
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,110 @@
1
+ require 'yaml'
2
+ require 'kwalify'
3
+
4
+ module Blufin
5
+
6
+ class Config
7
+
8
+ @@data = {}
9
+
10
+ # Validate and initialize the configuration file.
11
+ # @return void
12
+ def self.init(schema_file, template_file, config_file, gem_name, setup_command = 'x')
13
+ schema_file = File.expand_path(schema_file)
14
+ template_file = File.expand_path(template_file)
15
+ config_file = File.expand_path(config_file)
16
+ raise RuntimeError, "File not found: #{schema_file}" unless Blufin::Files::file_exists(schema_file)
17
+ raise RuntimeError, "File not found: #{template_file}" unless Blufin::Files::file_exists(template_file)
18
+ if Blufin::Files::file_exists(config_file)
19
+ # Validate the user created config file.
20
+ load_config(config_file, schema_file, gem_name)
21
+ else
22
+ system('clear')
23
+ if Blufin::Terminal::prompt_yes_no(" Missing Configuration File: #{Blufin::Terminal::format_directory(config_file)}", 'This script can automatically create it.', 'Create configuration file')
24
+ make_config(template_file, config_file)
25
+ edit_config(config_file)
26
+ Blufin::Terminal::custom('File Created', 56, "Now try running: #{Blufin::Terminal::format_command(gem_name)}", nil, false)
27
+ end
28
+ exit
29
+ end
30
+ end
31
+
32
+ # Straight-up returns the data object so you can get keys using string literals.
33
+ # Hard-coding keys is OK because configuration rarely changes and it's validated, so you can be assured the data is there.
34
+ # @return Hash
35
+ def self.get
36
+ @@data
37
+ end
38
+
39
+ # Use for path. Basically wraps them in File.expand_path.
40
+ # @return String
41
+ def self.get_path(*args)
42
+ val = get
43
+ args.each { |arg| val = val[arg] }
44
+ File.expand_path(val)
45
+ end
46
+
47
+ # Opens the config file in VIM.
48
+ # @return void
49
+ def self.edit_config(config_file)
50
+ system("vim #{config_file}")
51
+ end
52
+
53
+ # This validates the configuration file against the defined schema.
54
+ # @return Array
55
+ def self.validate_file(config_file, schema_file)
56
+ begin
57
+ schema_file_parsed = YAML.load_file(schema_file)
58
+ rescue => e
59
+ Blufin::Terminal::error("Failed to parse config file: #{Blufin::Terminal::format_directory(config_file)}", e.message)
60
+ end
61
+ validator = Kwalify::Validator.new(schema_file_parsed)
62
+ begin
63
+ document = YAML.load_file(config_file)
64
+ # This is here because the IDE is incorrectly resolving to Convoy::validate().
65
+ # noinspection RubyArgCount
66
+ errors = validator.validate(document)
67
+ rescue => e
68
+ Blufin::Terminal::error("Failed to parse config file: #{Blufin::Terminal::format_directory(config_file)}", e.message)
69
+ end
70
+ return document, errors
71
+ end
72
+
73
+ # This shows the schema validation errors in a consistent manner across all the gems.
74
+ # @return void
75
+ def self.display_errors(errors, gem_name, setup_command = 'x')
76
+ errors_output = []
77
+ errors.each { |e|
78
+ errors_output << "[#{e.path}] #{e.message}"
79
+ }
80
+ invalid_configuration(gem_name, errors, setup_command)
81
+ end
82
+
83
+ # Standardized way of outputting invalid configuration.
84
+ # @return void
85
+ def self.invalid_configuration(gem_name, errors = nil, setup_command = 'x')
86
+ Blufin::Terminal::error("Your configuration file is invalid. To fix it run: #{Blufin::Terminal::format_command("#{gem_name} #{setup_command}")}", errors, true)
87
+ end
88
+
89
+ private
90
+
91
+ # Loads the config file into global Hash.
92
+ # @return void
93
+ def self.load_config(config_file, schema_file, gem_name, setup_command = 'x')
94
+ document, errors = validate_file(config_file, schema_file)
95
+ if errors && !errors.empty?
96
+ display_errors(errors, gem_name, setup_command)
97
+ else
98
+ @@data = document
99
+ end
100
+ end
101
+
102
+ # Creates a the config file from the given template.
103
+ # @return
104
+ def self.make_config(template_file, config_file)
105
+ `cp #{template_file} #{config_file}`
106
+ end
107
+
108
+ end
109
+
110
+ end
@@ -162,6 +162,12 @@ module Blufin
162
162
  def self.write_file(path_and_file, array_of_lines)
163
163
  raise RuntimeError, "Expected String, instead got: #{path_and_file.class}" unless path_and_file.is_a?(String)
164
164
  path_and_file = File.expand_path(path_and_file)
165
+ # If this comes in as a string, convert it to an Array of lines.
166
+ if array_of_lines.is_a?(String)
167
+ array_of_lines_new = []
168
+ array_of_lines.split("\n").each { |line| array_of_lines_new << line.gsub("\n", '') }
169
+ array_of_lines = array_of_lines_new
170
+ end
165
171
  raise RuntimeError, "Expected an array of lines to write to file, instead got: #{array_of_lines.class}" unless array_of_lines.is_a? Array
166
172
  prepare_for_file_write(path_and_file)
167
173
  begin
@@ -327,7 +333,7 @@ module Blufin
327
333
  files.uniq.sort
328
334
  end
329
335
 
330
- # Get and array of directories in a directory.
336
+ # Get and array of directories in a directory.
331
337
  # @return Array
332
338
  def self.get_dirs_in_dir(path, recursive = false)
333
339
  raise RuntimeError, "Expected String, instead got: #{path.class}" unless path.is_a?(String)
@@ -381,6 +387,16 @@ module Blufin
381
387
  return File.basename(File.expand_path(path_and_file), '*') unless include_extension
382
388
  end
383
389
 
390
+ # Returns TRUE if file is empty. Ignores spaces and new-line characters.
391
+ # @return bool
392
+ def self.is_empty(path_and_file)
393
+ Blufin::Files::read_file(path_and_file).each do |line|
394
+ line.strip!
395
+ return false if line != ''
396
+ end
397
+ true
398
+ end
399
+
384
400
  private
385
401
 
386
402
  # Creates path (if not exists) and deletes file (if exists).
@@ -0,0 +1,331 @@
1
+ module Blufin
2
+
3
+ class Projects
4
+
5
+ SCHEMA_FILE = "#{Blufin::Base::get_base_path}#{Blufin::Base::OPT_PATH}/awx/project-schema.yml"
6
+ SCRIPT_RUN = 'run'
7
+ SCRIPT_TEST = 'test'
8
+ SCRIPT_BUILD = 'build'
9
+ SCRIPT_DEPLOY = 'deploy'
10
+ RUN_SCRIPTS = 'RunScripts'
11
+ TEST_SCRIPTS = 'TestScripts'
12
+ BUILD_SCRIPTS = 'BuildScripts'
13
+ DEPLOY_SCRIPTS = 'DeployScripts'
14
+ DEPLOYMENT_ID = 'Id'
15
+ TYPE = 'Type'
16
+ PROJECT = 'Project'
17
+ REPO_ROOT = 'RepoRoot'
18
+ REPO_PATH = 'RepoPath'
19
+ PORT_RANGE = 'PortRange'
20
+ RUN = 'Run'
21
+ TEST = 'Test'
22
+ BUILD = 'Build'
23
+ DEPLOY = 'Deploy'
24
+ SERVERS = 'Servers'
25
+ REGION = 'Region'
26
+ STACK = 'Stack'
27
+ ENVIRONMENTS = 'Environments'
28
+ TARGETS = 'Targets'
29
+ COMMANDS = 'Commands'
30
+ API = 'API'
31
+ TITLE = 'Title'
32
+ ALIAS = 'Alias'
33
+ DOMAIN = 'Domain'
34
+ PROJECT_NAME_PASCAL_CASE = 'ProjectNamePascalCase'
35
+ PROJECT_NAME = 'ProjectName'
36
+ TYPE_ALEXA = 'alexa'
37
+ TYPE_API = 'api'
38
+ TYPE_LAMBDA = 'lambda'
39
+ TYPE_UI = 'ui'
40
+ VALID_TYPES = [
41
+ TYPE_ALEXA,
42
+ TYPE_API,
43
+ TYPE_LAMBDA,
44
+ TYPE_UI,
45
+ ]
46
+
47
+ @@projects = nil
48
+ @@project_names = []
49
+ @@scripts = nil
50
+ @@deployments = nil
51
+ @@apis = nil
52
+ @@environments = []
53
+
54
+ # Takes a Hash that should have the APIs, Lambdas and UIs keys in the root.
55
+ # This can come from both .awx.yml['Profiles'] or .blufin.yml (root).
56
+ # @return void
57
+ def self.init(hash, file)
58
+ raise RuntimeError, 'Cannot run Blufin::Projects::init() more than once.' unless @@projects.nil? && @@scripts.nil?
59
+ @@projects = {}
60
+ @@scripts = {}
61
+ if hash.has_key?('Projects')
62
+ projects = hash['Projects']
63
+ # Throw error if we have multiple sources defined, can only have 1.
64
+ Blufin::Terminal::error("Found multiple sources for #{Blufin::Terminal::format_highlight(UIs)} property in: #{Blufin::Terminal::format_directory(file)}. Can #{Blufin::Terminal::format_highlight('only have one')} of the following, not both:", [Blufin::Terminal::format_invalid('Local'), Blufin::Terminal::format_invalid('S3Bucket')], true) if projects.has_key?('Local') && projects.has_key?('S3Bucket')
65
+ if projects.has_key?('Local')
66
+ source_file = File.expand_path(projects['Local']['File'])
67
+ # Throw error if source file doesn't exist.
68
+ Blufin::Terminal::error("Cannot find source file: #{Blufin::Terminal::format_directory(source_file)}") unless Blufin::Files::file_exists(source_file)
69
+ # Validate the source file against the expected schema.
70
+ process_source_file(source_file)
71
+ elsif projects.has_key?('S3Bucket')
72
+
73
+ # TODO - Finish this once we start running this on an EC2 instance (build/deploy server).
74
+ # TODO - Whatever file we validate should be available on disk locally.
75
+ # TODO - If the source is an S3 bucket, pull it down into a /tmp folder (on EC2 instance) and validate from there.
76
+
77
+ end
78
+ end
79
+ end
80
+
81
+ # Gets Project(s).
82
+ # @return Hash
83
+ def self.get_projects
84
+ @@projects
85
+ end
86
+
87
+ # Gets Project Name(s).
88
+ # @return Array
89
+ def self.get_project_names
90
+ @@project_names
91
+ end
92
+
93
+ # Gets Script(s).
94
+ # @return Hash
95
+ def self.get_scripts
96
+ @@scripts
97
+ end
98
+
99
+ # Gets API(s).
100
+ # @return Hash
101
+ def self.get_apis
102
+ @@apis
103
+ end
104
+
105
+ # Gets Environments(s).
106
+ # @return Array
107
+ def self.get_environments
108
+ @@environments
109
+ end
110
+
111
+ # Get matching deployments for parameters.
112
+ # @return Hash
113
+ def self.get_deployments(project, stack, region, environment)
114
+ if @@deployments.nil?
115
+ @@deployments = {}
116
+ if @@projects.is_a?(Hash) && @@projects.has_key?(project)
117
+ project = @@projects[project]
118
+ project.each do |deployment_id, data|
119
+ if data.is_a?(Hash) && data.has_key?(SERVERS)
120
+ servers = data[SERVERS]
121
+ if servers.is_a?(Array)
122
+ servers.each do |server|
123
+ if server.has_key?(REGION) && server[REGION] == region
124
+ if server.has_key?(STACK) && server[STACK] == stack
125
+ if server.has_key?(ENVIRONMENTS) && server[ENVIRONMENTS].include?(environment)
126
+ @@deployments[deployment_id] = data
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ @@deployments
137
+ end
138
+
139
+ # Maps root-level property to enum.
140
+ # @return string
141
+ def self.script_key_mapper(script_type)
142
+ if [RUN_SCRIPTS, RUN].include?(script_type)
143
+ return SCRIPT_RUN
144
+ elsif [TEST_SCRIPTS, TEST].include?(script_type)
145
+ return TEST_SCRIPTS
146
+ elsif [BUILD_SCRIPTS, BUILD].include?(script_type)
147
+ return BUILD_SCRIPTS
148
+ elsif [DEPLOY_SCRIPTS, DEPLOY].include?(script_type)
149
+ return DEPLOY_SCRIPTS
150
+ else
151
+ raise RuntimeError, "Unhandled script type: #{script_type}"
152
+ end
153
+ end
154
+
155
+ private
156
+
157
+ # Validate the Project YML.
158
+ # @return void
159
+ def self.process_source_file(source_file)
160
+ # Skip empty file.
161
+ return if Blufin::Files::is_empty(source_file)
162
+ # Otherwise, validate file.
163
+ document, errors = Blufin::Config::validate_file(source_file, SCHEMA_FILE)
164
+ if errors && !errors.empty?
165
+ errors_output = []
166
+ errors.each { |e|
167
+ errors_output << "[#{e.path}] #{e.message}"
168
+ }
169
+ Blufin::Terminal::error("Your configuration file is invalid. Please fix: #{Blufin::Terminal::format_directory(source_file)}", errors)
170
+ else
171
+ begin
172
+ file_parsed = YAML.load_file(source_file)
173
+ rescue => e
174
+ Blufin::Terminal::error("Failed to parse config file: #{Blufin::Terminal::format_directory(source_file)}", e.message)
175
+ end
176
+
177
+ # Buffer script(s).
178
+ [RUN_SCRIPTS, TEST_SCRIPTS, BUILD_SCRIPTS, DEPLOY_SCRIPTS].each do |script_type|
179
+ if file_parsed.has_key?(script_type)
180
+ if file_parsed[script_type].is_a?(Array)
181
+ script_key = script_key_mapper(script_type)
182
+ @@scripts[script_key] = {} unless @@scripts.has_key?(script_key)
183
+ file_parsed[script_type].each do |script|
184
+ script_id = script[DEPLOYMENT_ID]
185
+ # Throw error if duplicate script is found for type.
186
+ Blufin::Terminal::error("#{Blufin::Terminal::format_highlight("#{script_type} - #{script_id}")} \xe2\x80\x94 Duplicate script found: #{Blufin::Terminal::format_invalid(script_id)}") if @@scripts[script_key].has_key?(script_id)
187
+ @@scripts[script_key][script_id] = script
188
+ # Check that if script is AWS, it doesn't have --region or --profile flag.
189
+ if script.has_key?(COMMANDS)
190
+ script[COMMANDS].each do |command|
191
+ if command.strip =~ /^aws/i
192
+ Blufin::Terminal::error("#{Blufin::Terminal::format_highlight("#{script_type} - #{script_id}")} \xe2\x80\x94 AWS scripts cannot have: #{Blufin::Terminal::format_invalid('--region')} flag.", command) if command =~ /\s--region\s/
193
+ Blufin::Terminal::error("#{Blufin::Terminal::format_highlight("#{script_type} - #{script_id}")} \xe2\x80\x94 AWS scripts cannot have: #{Blufin::Terminal::format_invalid('--profile')} flag.", command) if command =~ /\s--profile\s/
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+
202
+ used_ports = {}
203
+
204
+ # Loop (and validate) projects.
205
+ file_parsed['Projects'].each do |project|
206
+ # Validate keys are in specific order.
207
+ expected = {
208
+ DEPLOYMENT_ID => true,
209
+ TYPE => true,
210
+ PROJECT => true,
211
+ REPO_ROOT => true,
212
+ REPO_PATH => true,
213
+ PORT_RANGE => false,
214
+ RUN => false,
215
+ TEST => false,
216
+ BUILD => false,
217
+ DEPLOY => false,
218
+ SERVERS => false,
219
+ API => false
220
+ }
221
+ Blufin::Validate::assert_valid_keys(expected, project.keys, source_file)
222
+ project_id = project[DEPLOYMENT_ID]
223
+ project_name = project[PROJECT]
224
+ project_type = project[TYPE]
225
+
226
+ @@project_names << project_name
227
+
228
+ # Validate Type.
229
+ Blufin::Terminal::error("#{project_id} \xe2\x80\x94 Invalid Project Type: #{Blufin::Terminal::format_invalid(project_type)}. Valid types are:", VALID_TYPES, true) unless VALID_TYPES.include?(project_type)
230
+ # Validate Script(s).
231
+ [RUN, TEST, BUILD, DEPLOY].each do |script_type|
232
+ if project.has_key?(script_type)
233
+ # Validate the LAMBDA functions don't need build scripts.
234
+ Blufin::Terminal::error("#{project_id} \xe2\x80\x94 Project type: #{Blufin::Terminal::format_highlight(TYPE_LAMBDA)} cannot have #{Blufin::Terminal::format_invalid(script_type)} property.", 'This type of project does not support this.', true) if [BUILD].include?(script_type) && project_type == TYPE_LAMBDA
235
+ if project[script_type].is_a?(Array)
236
+ script_key = script_key_mapper(script_type)
237
+ valid_scripts = []
238
+ valid_scripts = @@scripts[script_key].keys if @@scripts.has_key?(script_key)
239
+ project[script_type].each do |script|
240
+ script_name = script['Script']
241
+ unless valid_scripts.include?(script_name)
242
+ error = valid_scripts.any? ? 'Valid values are:' : "There currently are no #{script_key} script(s) defined."
243
+ Blufin::Terminal::error("#{project_id} \xe2\x80\x94 #{Blufin::Terminal::format_highlight(script_type)} \xe2\x80\x94 Invalid script reference: #{Blufin::Terminal::format_invalid(script_name)}. #{error}", valid_scripts)
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
249
+ # Validate Server(s) and create new 'Targets' Hash from it.
250
+ if project.has_key?(SERVERS)
251
+ targets = {}
252
+ project[SERVERS].each do |server|
253
+ region = server['Region']
254
+ stack = server['Stack']
255
+ environments = server['Environments']
256
+ environments.each do |environment|
257
+ targets[environment] = {} unless targets.has_key?(environment)
258
+ targets[environment] = {
259
+ :region => region,
260
+ :stack => stack,
261
+ }
262
+ @@environments << environment
263
+ end
264
+ end
265
+ project[TARGETS] = targets
266
+ end
267
+ # Validate APIs and build @@api Hash.
268
+ if project_type == TYPE_API
269
+ # Make sure we have the API & PortRange properties.
270
+ Blufin::Terminal::error("#{project_id} \xe2\x80\x94 Missing property: #{Blufin::Terminal::format_highlight(API)}", "This property is required for project(s) with type: #{API}", true) unless project.has_key?(API)
271
+ Blufin::Terminal::error("#{project_id} \xe2\x80\x94 Missing property: #{Blufin::Terminal::format_highlight(PORT_RANGE)}", "This property is required for project(s) with type: #{API}", true) unless project.has_key?(PORT_RANGE)
272
+ # Validate keys are in specific order.
273
+ expected = {
274
+ TITLE => true,
275
+ ALIAS => true,
276
+ DOMAIN => true,
277
+ PROJECT_NAME => true,
278
+ PROJECT_NAME_PASCAL_CASE => true
279
+ }
280
+ Blufin::Validate::assert_valid_keys(expected, project[API].keys, source_file)
281
+ @@apis = {} if @@apis.nil?
282
+ @@apis[project[DEPLOYMENT_ID]] = {
283
+ REPO_ROOT => project[REPO_ROOT],
284
+ TITLE => project[API][TITLE],
285
+ ALIAS => project[API][ALIAS],
286
+ PROJECT_NAME => project[API][PROJECT_NAME],
287
+ PROJECT_NAME_PASCAL_CASE => project[API][PROJECT_NAME_PASCAL_CASE],
288
+ DOMAIN => project[API][DOMAIN],
289
+ PORT_RANGE => project[PORT_RANGE]
290
+ }
291
+ else
292
+ # Make sure we DON'T have the API key.
293
+ Blufin::Terminal::error("#{project_id} \xe2\x80\x94 Property not supported: #{Blufin::Terminal::format_invalid(API)}", "This property is only allowed for project(s) with type: #{API}", true) if project.has_key?(API)
294
+ end
295
+
296
+ # Validate the ports range.
297
+ if project.has_key?(PORT_RANGE)
298
+ port_range = project[PORT_RANGE].strip
299
+ # Make sure Port Range is in format: XXXX-XXXX
300
+ port_range_regex = /^\d{4}-\d{4}$/
301
+ Blufin::Terminal::error("#{project_id} \xe2\x80\x94 Port Range does not match regex: #{Blufin::Terminal::format_invalid(port_range)}", "Must match regex: #{port_range_regex}", true) unless port_range =~ port_range_regex
302
+ prs = port_range.split('-')
303
+ raise RuntimeError, "Expected port range to have exactly one hyphen (-), instead got: #{port_range}" unless prs.length == 2
304
+ # Make sure port range spans exactly 20 ports (by having a difference of 19).
305
+ Blufin::Terminal::error("#{project_id} \xe2\x80\x94 Port Range either doesn't span 20 ports or is not in correct, numerical order: #{Blufin::Terminal::format_invalid(port_range)}") unless prs[1].to_i - prs[0].to_i == 19
306
+ # Make sure none of the ports conflict with other projects.
307
+ (prs[0].to_i..prs[1].to_i).each do |i|
308
+ if used_ports.has_key?(i)
309
+ Blufin::Terminal::error("#{project_id} \xe2\x80\x94 Port overlap detected for range: #{Blufin::Terminal::format_invalid(port_range)}", ["The conflicting project is: #{used_ports[i]}"])
310
+ end
311
+ end
312
+ # Add ports to used_ports.
313
+ (prs[0].to_i..prs[1].to_i).each { |i| used_ports[i] = "#{Blufin::Terminal::format_highlight(project_id)} \xe2\x80\x94 #{port_range}" }
314
+ end
315
+
316
+ @@projects[project_name] = {} unless @@projects.has_key?(project_name)
317
+ Blufin::Terminal::error("Duplicate project ID: #{Blufin::Terminal::format_invalid(project_id)}") if @@projects[project_name].has_key?(project_id)
318
+ @@projects[project_name][project_id] = project
319
+
320
+ end
321
+
322
+ @@project_names.uniq!.sort!
323
+ @@environments.uniq!.sort!
324
+
325
+ end
326
+
327
+ end
328
+
329
+ end
330
+
331
+ end
@@ -1,7 +1,11 @@
1
+ require 'securerandom'
2
+
1
3
  module Blufin
2
4
 
3
5
  class Strings
4
6
 
7
+ RETURN_CHARACTER = '↵'
8
+
5
9
  # Convert 'snake_case' or 'SnAKE_cAse' to 'SnakeCase'.
6
10
  # @param String
7
11
  # @return String
@@ -82,6 +86,13 @@ module Blufin
82
86
  return ((discrepancies.to_f / total_letters.to_f) * 100).round
83
87
  end
84
88
 
89
+ # Generate Random string.
90
+ # Currently returns something like: 1ec6c763
91
+ # @return String
92
+ def self.random_string
93
+ "#{SecureRandom.uuid.split('-')[0].downcase}#{SecureRandom.uuid.split('-')[0].downcase}"
94
+ end
95
+
85
96
  private
86
97
 
87
98
  # Internal string validation.
@@ -11,20 +11,20 @@ module Blufin
11
11
 
12
12
  extend Columnist
13
13
 
14
- MSG_INFO = 'info'
15
- MSG_WARNING = 'warning'
16
- MSG_ERROR = 'error'
17
- MSG_TODO = 'todo'
18
- MSG_AUTOMATIC = 'automatic'
19
- MSG_GENERATED = 'generated'
20
- MSG_PROCESSED = 'processed'
21
- MSG_PROGRESS = 'progress'
22
- MSG_CUSTOM = 'custom'
14
+ MSG_INFO = 'info'
15
+ MSG_WARNING = 'warning'
16
+ MSG_ERROR = 'error'
17
+ MSG_TODO = 'todo'
18
+ MSG_AUTOMATIC = 'automatic'
19
+ MSG_GENERATED = 'generated'
20
+ MSG_PROCESSED = 'processed'
21
+ MSG_PROGRESS = 'progress'
22
+ MSG_CUSTOM = 'custom'
23
23
  MSG_CUSTOM_AUTO_PAD = 'custom_auto_pad'
24
24
 
25
25
  # Runs a series of commands in the terminal.
26
26
  # @return void
27
- def self.command(commands, location = nil, verbose = true, verbose_cd = true, capture_real_output = false)
27
+ def self.command(commands, location = nil, verbose = true, verbose_cd = true, capture_real_output = false, pbl: false, tbl: false)
28
28
  unless commands.is_a?(Array)
29
29
  commands = [commands]
30
30
  end
@@ -35,13 +35,15 @@ module Blufin
35
35
  end
36
36
  output = []
37
37
  if verbose_cd && verbose && commands.any? && !location.nil?
38
- puts "\x1B[38;5;231m\x1B[42m Executing \x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m #{Blufin::Terminal::format_command("cd #{location}")}\n"
38
+ puts "\x1B[38;5;231m\x1B[48;5;22m Executing \x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m #{Blufin::Terminal::format_command("cd #{location}")}\n"
39
39
  end
40
40
  if commands.any?
41
41
  commands.each do |command|
42
42
  if verbose
43
- puts "\x1B[38;5;231m\x1B[42m Executing \x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m #{Blufin::Terminal::format_command("#{command}")}\n"
43
+ puts "\x1B[38;5;231m\x1B[48;5;22m Executing \x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m #{Blufin::Terminal::format_command("#{command}")}\n"
44
+ puts if pbl
44
45
  end
46
+
45
47
  if location.nil?
46
48
  if capture_real_output
47
49
  output << `#{command}`
@@ -60,6 +62,7 @@ module Blufin
60
62
  if capture_real_output
61
63
  output.map! { |v| v.gsub('\n', "\n") }
62
64
  end
65
+ puts if tbl
63
66
  output
64
67
  end
65
68
 
@@ -69,25 +72,29 @@ module Blufin
69
72
  command(commands, location, verbose, verbose_cd, true)
70
73
  end
71
74
 
72
- # Executes a command and shows that something is happening (via a cli-spinner)
75
+ # Executes a command and shows that something is happening (via a cli-spinner).
76
+ # If capture: is false, returns TRUE:FALSE whether command was successful or not.
77
+ # If capture: is true, returns raw output of command.
73
78
  # See: https://github.com/piotrmurach/tty-spinner/blob/master/lib/tty/spinner/formats.rb (for spinner options).
74
79
  # @return void
75
- def self.execute(command, path = nil, capture = false)
76
- t1 = Time.now
77
- spinner = TTY::Spinner.new("[:spinner] \x1B[38;5;208m#{command}#{!path.nil? ? " \x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;240m#{path}" : nil}\x1B[0m", format: :dots)
78
- spinner.auto_spin
80
+ def self.execute(command, path = nil, capture: false, verbose: true, text: nil)
81
+ text = text.is_a?(String) ? text : command
82
+ t1 = Time.now
83
+ spinner = TTY::Spinner.new("[:spinner] \x1B[38;5;208m#{text}#{!path.nil? ? " \x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;240m#{path}" : nil}\x1B[0m", format: :dots) if verbose
84
+ spinner.auto_spin if verbose
79
85
  path = File.expand_path('~/') if path.nil?
80
86
  if capture
81
- res = system(`cd #{path} && #{command}`)
87
+ res = `cd #{path} && #{command}`
82
88
  else
83
89
  res = system("cd #{path} && #{command} > /tmp/execute-output")
84
90
  end
85
- t2 = Time.now
91
+ t2 = Time.now
86
92
  delta = "#{'%.3f' % (t2 - t1).abs}s"
87
93
  if res || capture
88
- spinner.success("\x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;46mComplete \x1B[38;5;240m(#{delta})\x1B[0m\x1B[0m")
94
+ spinner.success("\x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;46mComplete \x1B[38;5;240m(#{delta})\x1B[0m\x1B[0m") if verbose
89
95
  else
90
- spinner.error("\x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;196mFailed (#{delta})\x1B[0m")
96
+ spinner.error("\x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;196mFailed (#{delta})\x1B[0m") if verbose
97
+ # If there is an error, output it (even if verbose == false).
91
98
  puts "\x1B[38;5;240m"
92
99
  system('cat /tmp/execute-output')
93
100
  puts "\x1B[0m"
@@ -100,12 +107,12 @@ module Blufin
100
107
  def self.execute_proc(title, proc, verbose: true)
101
108
  raise RuntimeError, "Expected String, instead got:#{title.class}" unless title.is_a?(String)
102
109
  raise RuntimeError, "Expected proc to be an instance of Proc, instead got: #{proc.class}" unless proc.is_a?(Proc)
103
- t1 = Time.now
110
+ t1 = Time.now
104
111
  spinner = nil
105
112
  spinner = TTY::Spinner.new("[:spinner] \x1B[38;5;208m#{title}\x1B[0m", format: :dots) if verbose
106
113
  spinner.auto_spin if verbose
107
- res = proc.call
108
- t2 = Time.now
114
+ res = proc.call
115
+ t2 = Time.now
109
116
  delta = "#{'%.3f' % (t2 - t1).abs}s"
110
117
  spinner.success("\x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;46mComplete \x1B[38;5;240m(#{delta})\x1B[0m\x1B[0m") if verbose && res
111
118
  spinner.error("\x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;196mFailed (#{delta})\x1B[0m") if verbose && !res
@@ -151,7 +158,7 @@ module Blufin
151
158
  # Displays error and exits script by default.
152
159
  # @return void
153
160
  def self.abort(title = nil, message = nil, exit_script = true, preceding_blank_line = false)
154
- title = 'Abandon ship!' if title.nil?
161
+ title = 'Abandon ship!' if title.nil?
155
162
  message = "You have chosen to \x1B[38;5;196mABORT\x1B[38;5;240m the script." if message.nil?
156
163
  Blufin::Terminal::error(title, message, exit_script, preceding_blank_line, 'ABORT')
157
164
  end
@@ -193,7 +200,7 @@ module Blufin
193
200
  # @return void
194
201
  def self.warning(title = nil, message = nil, preceding_blank_line = true)
195
202
  puts if preceding_blank_line
196
- puts " \x1B[38;5;231m\x1B[48;5;202m Warning \x1B[0m #{title.nil? ? '' : "\xe2\x86\x92 "}#{title}\n"
203
+ puts " \x1B[38;5;231m\x1B[48;5;130m Warning \x1B[0m #{title.nil? ? '' : "\xe2\x86\x92 "}#{title}\n"
197
204
  parse_messages(message)
198
205
  end
199
206
 
@@ -210,6 +217,7 @@ module Blufin
210
217
  # Displays custom message (ideally, keyword should be 7 characters long to match the rest of the output).
211
218
  # @return void
212
219
  def self.custom(keyword = 'N/A', color = 1, title = nil, message = nil, preceding_blank_line = true)
220
+ color = 55 if color.nil?
213
221
  puts if preceding_blank_line
214
222
  puts " \x1B[38;5;231m\x1B[48;5;#{color}m #{keyword} \x1B[0m #{title.nil? ? '' : "\xe2\x86\x92 "}#{title}\n"
215
223
  parse_messages(message)
@@ -272,11 +280,25 @@ module Blufin
272
280
 
273
281
  # Returns action message in consistent, uniform manner.
274
282
  # @return String
275
- def self.format_highlight(highlighted_text, capitalize = false)
283
+ def self.format_highlight(highlighted_text, capitalize = false, target: nil)
284
+ case target
285
+ when 'cf' # CloudFormation
286
+ color = 82
287
+ color_end = "\x1B[38;5;246m"
288
+ when 'cf-ctt' # CloudFormation (Creation Time Title)
289
+ color = 196
290
+ color_end = "\x1B[38;5;246m"
291
+ when 'cf-ct' # CloudFormation (Creation Time)
292
+ color = 82
293
+ color_end = "\x1B[38;5;246m"
294
+ else
295
+ color = 117
296
+ color_end = "\x1B[0m"
297
+ end
276
298
  if capitalize
277
- return "\x1B[38;5;117m#{highlighted_text.upcase}\x1B[0m"
299
+ return "\x1B[38;5;#{color}m#{highlighted_text.upcase}#{color_end}"
278
300
  else
279
- return "\x1B[38;5;117m#{highlighted_text}\x1B[0m"
301
+ return "\x1B[38;5;#{color}m#{highlighted_text}#{color_end}"
280
302
  end
281
303
  end
282
304
 
@@ -303,10 +325,10 @@ module Blufin
303
325
  # Gives a prompt where 'y/Y' will return TRUE, 'n/N' will return false, and ANY other key will do nothing.
304
326
  # @return void
305
327
  def self.prompt_yes_no(title = nil, message = nil, confirmation_message = nil, preceding_blank_line = true)
306
- title = 'Please confirm YES or NO.' if title.nil?
328
+ title = 'Please confirm YES or NO.' if title.nil?
307
329
  confirmation_message = 'Would you like to continue?' if confirmation_message.nil?
308
330
  puts if preceding_blank_line
309
- puts " \x1B[38;5;231m\x1B[48;5;56m Confirm \x1B[0m #{title.nil? ? '' : "\xe2\x86\x92 "}#{title}\n"
331
+ puts " \x1B[38;5;231m\x1B[48;5;55m Confirm \x1B[0m #{title.nil? ? '' : "\xe2\x86\x92 "}#{title}\n"
310
332
  parse_messages(message)
311
333
  response = ''
312
334
  until %w[y Y n N x X a A].include? response
@@ -320,9 +342,9 @@ module Blufin
320
342
  puts "\n"
321
343
  return false
322
344
  when 'a'
323
- Blufin::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)
345
+ Blufin::Terminal::error('Abandon ship!', ["You have chosen to \x1B[38;5;196mABORT\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)
324
346
  when 'x'
325
- Blufin::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)
347
+ Blufin::Terminal::error('Abandon ship!', ["You have chosen to \x1B[38;5;196mABORT\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)
326
348
  else
327
349
  raise RuntimeError, "Un-handled response: #{response.downcase}"
328
350
  end
@@ -423,10 +445,26 @@ module Blufin
423
445
  # ⬡ bourbon
424
446
  # @options = %w(vodka beer wine whisky bourbon)
425
447
  # @return Array
426
- def self.prompt_multi_select(question, options)
448
+ def self.prompt_multi_select(question, options, help: nil, per_page: 20, cycle: true)
427
449
  raise RuntimeError, "Expected Array, instead got #{options.class}" unless options.is_a?(Array)
450
+ puts display_prompt_help(help) unless help.nil?
428
451
  prompt = TTY::Prompt.new
429
- prompt.multi_select(display_prompt_text(question), options)
452
+ if options[0].is_a?(String)
453
+ prompt.multi_select(display_prompt_text(question), options, per_page: per_page, cycle: cycle)
454
+ elsif options[0].is_a?(Hash)
455
+ prompt.multi_select(display_prompt_text(question), options, per_page: per_page, cycle: cycle) do |menu|
456
+ options.each do |option|
457
+ raise RuntimeError, "Expected option to be Hash, instead got: (#{option.class}) #{option.inspect}" unless option.is_a?(Hash)
458
+ raise RuntimeError, 'Option is missing key => :text' unless option.has_key?(:text)
459
+ raise RuntimeError, 'Option is missing key => :value' unless option.has_key?(:value)
460
+ raise RuntimeError, "Expected :disabled option to be String, instead got: #{option[:disabled].class}" if option.has_key?(:disabled) && !option[:disabled].is_a?(String) && !option[:disabled].nil?
461
+ menu.choice option[:text], option[:value] unless option.has_key?(:disabled)
462
+ menu.choice option[:text], option[:value], disabled: "\x1B[38;5;196m#{option[:disabled]}\x1B[0m" if option.has_key?(:disabled)
463
+ end
464
+ end
465
+ else
466
+ raise RuntimeError, "Expected options Array to consist of either Strings or Hashes, instead got: #{options.inspect}"
467
+ end
430
468
  end
431
469
 
432
470
  # Select an editor?
@@ -454,8 +492,8 @@ module Blufin
454
492
  def self.prompt_expand(question, options)
455
493
  raise RuntimeError, "Expected Array, instead got #{options.class}" unless options.is_a?(Array)
456
494
  options.each do |option|
457
- found_key = false
458
- found_name = false
495
+ found_key = false
496
+ found_name = false
459
497
  found_value = false
460
498
  option.each do |k, v|
461
499
  case k
@@ -521,11 +559,11 @@ module Blufin
521
559
  # @return void
522
560
  def self.print_files_written(array_of_paths_or_files, message = nil, limit = 10, preceding_blank_line = true)
523
561
  raise RuntimeError, "Expected Array of files, instead got: #{array_of_paths_or_files.class}" unless array_of_paths_or_files.is_a?(Array)
524
- message = "Wrote the following #{Blufin::Terminal::format_highlight('files/directories')}" if message.nil?
525
- limit = limit.nil? ? 99999999999 : limit.to_i
562
+ message = "Wrote the following #{Blufin::Terminal::format_highlight('files/directories')}" if message.nil?
563
+ limit = limit.nil? ? 99999999999 : limit.to_i
526
564
  file_output = []
527
- file_count = 0
528
- file_more = 0
565
+ file_count = 0
566
+ file_more = 0
529
567
  array_of_paths_or_files.compact!
530
568
  array_of_paths_or_files.sort!
531
569
  array_of_paths_or_files.each do |path_or_file|
@@ -555,13 +593,13 @@ module Blufin
555
593
  # @return void
556
594
  def self.code_highlight(string, type, indent = 5)
557
595
  raise RuntimeError, "Expected String, instead got:#{string.class}" unless string.is_a?(String)
558
- type = type.downcase
596
+ type = type.downcase
559
597
  types = {
560
- 'yml' => Rouge::Lexers::YAML.new,
598
+ 'yml' => Rouge::Lexers::YAML.new,
561
599
  'json' => Rouge::Lexers::JSON.new
562
600
  }
563
601
  raise RuntimeError, "Lexer not defined for type: #{type}" unless types.has_key?(type)
564
- repeat = ' ' * indent
602
+ repeat = ' ' * indent
565
603
  formatter = Rouge::Formatters::Terminal256.new
566
604
  formatter.format(types[type].lex(string)).split("\n").each do |line|
567
605
  puts "#{repeat}#{line}"
@@ -601,8 +639,9 @@ module Blufin
601
639
 
602
640
  # Standardized way of displaying prompts text.
603
641
  # @return string
604
- def self.display_prompt_text(question)
605
- "\x1B[38;5;208m#{question}\x1B[38;5;240m =>\x1B[0m"
642
+ def self.display_prompt_text(question, answer = nil)
643
+ return "\x1B[38;5;208m#{question}\x1B[38;5;240m =>\x1B[0m" if answer.nil?
644
+ return "\x1B[38;5;208m#{question}\x1B[38;5;240m => \x1B[38;5;46m#{answer}\x1B[0m" unless answer.nil?
606
645
  end
607
646
 
608
647
  end
@@ -0,0 +1,29 @@
1
+ module Blufin
2
+
3
+ class Versioning
4
+
5
+ VERSION_REGEX = /\d{1,3}\.\d{1,3}\.\d{0,3}/
6
+
7
+ # Shows prompt to let you select a new major, minor or patch version.
8
+ # @return string
9
+ def self.get_new_version_from_prompt(title, current_version, capitalize_tile: true)
10
+ Blufin::Terminal::error("Current version does not match regex: #{current_version}", "Expected something like: #{Blufin::Terminal::format_highlight('12.1.0')}", true) unless current_version =~ VERSION_REGEX
11
+ vp = current_version.split('.')
12
+ color1 = 246
13
+ color2 = 40
14
+ patch = "\x1B[38;5;#{color1}mPatch \x1B[38;5;242m\xe2\x80\x94 \x1B[38;5;#{color2}m#{vp[0]}.#{vp[1]}.#{vp[2].to_i + 1} \x1B[38;5;242m\xe2\x80\x94\x1B[38;5;240m New version is interchangeable, users can upgrade/downgrade freely."
15
+ minor = "\x1B[38;5;#{color1}mMinor \x1B[38;5;242m\xe2\x80\x94 \x1B[38;5;#{color2}m#{vp[0]}.#{vp[1].to_i + 1}.0 \x1B[38;5;242m\xe2\x80\x94\x1B[38;5;240m New version is backwards compatible, users can upgrade freely."
16
+ major = "\x1B[38;5;#{color1}mMajor \x1B[38;5;242m\xe2\x80\x94 \x1B[38;5;#{color2}m#{vp[0].to_i + 1}.0.0 \x1B[38;5;242m\xe2\x80\x94\x1B[38;5;240m New version has breaking API changes, users #{Blufin::Terminal::format_invalid('CANNOT')}\x1B[38;5;240m upgrade without making code changes."
17
+ options = [
18
+ {:text => patch, :value => "#{vp[0]}.#{vp[1]}.#{vp[2].to_i + 1}"},
19
+ {:text => minor, :value => "#{vp[0]}.#{vp[1].to_i + 1}.0"},
20
+ {:text => major, :value => "#{vp[0].to_i + 1}.0.0"}
21
+ ]
22
+ title = title.upcase if capitalize_tile
23
+ Blufin::Terminal::custom(title, nil, "Current version is: #{Blufin::Terminal::format_highlight(current_version)}")
24
+ Blufin::Terminal::prompt_select('Choose new semantic version:', options)
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -1 +1 @@
1
- BLUFIN_LIB_VERSION = '1.5.0'
1
+ BLUFIN_LIB_VERSION = '1.5.2'
@@ -0,0 +1,130 @@
1
+ type: map
2
+ mapping:
3
+ RunScripts:
4
+ type: seq
5
+ sequence:
6
+ - type: map
7
+ mapping:
8
+ Id:
9
+ required: yes
10
+ Commands:
11
+ type: seq
12
+ required: yes
13
+ sequence:
14
+ - type: str
15
+ TestScripts:
16
+ type: seq
17
+ sequence:
18
+ - type: map
19
+ mapping:
20
+ Id:
21
+ required: yes
22
+ Commands:
23
+ type: seq
24
+ required: yes
25
+ sequence:
26
+ - type: str
27
+ BuildScripts:
28
+ type: seq
29
+ sequence:
30
+ - type: map
31
+ mapping:
32
+ Id:
33
+ required: yes
34
+ Commands:
35
+ type: seq
36
+ required: yes
37
+ sequence:
38
+ - type: str
39
+ DeployScripts:
40
+ type: seq
41
+ sequence:
42
+ - type: map
43
+ mapping:
44
+ Id:
45
+ required: yes
46
+ Commands:
47
+ type: seq
48
+ required: yes
49
+ sequence:
50
+ - type: str
51
+ Projects:
52
+ type: seq
53
+ required: yes
54
+ sequence:
55
+ - type: map
56
+ mapping:
57
+ Id:
58
+ required: yes
59
+ Type:
60
+ required: yes
61
+ Project:
62
+ required: yes
63
+ RepoRoot:
64
+ required: yes
65
+ RepoPath:
66
+ required: yes
67
+ PortRange:
68
+ Run:
69
+ type: seq
70
+ sequence:
71
+ - type: map
72
+ mapping:
73
+ Path:
74
+ required: yes
75
+ Script:
76
+ required: yes
77
+ Test:
78
+ type: seq
79
+ sequence:
80
+ - type: map
81
+ mapping:
82
+ Path:
83
+ required: yes
84
+ Script:
85
+ required: yes
86
+ Build:
87
+ type: seq
88
+ sequence:
89
+ - type: map
90
+ mapping:
91
+ Path:
92
+ required: yes
93
+ Script:
94
+ required: yes
95
+ Deploy:
96
+ type: seq
97
+ sequence:
98
+ - type: map
99
+ mapping:
100
+ Path:
101
+ required: yes
102
+ Script:
103
+ required: yes
104
+ Servers:
105
+ type: seq
106
+ sequence:
107
+ - type: map
108
+ mapping:
109
+ Region:
110
+ required: yes
111
+ Stack:
112
+ required: yes
113
+ Environments:
114
+ type: seq
115
+ required: yes
116
+ sequence:
117
+ - type: str
118
+ API:
119
+ type: map
120
+ mapping:
121
+ Title:
122
+ required: yes
123
+ Alias:
124
+ required: yes
125
+ Domain:
126
+ required: yes
127
+ ProjectName:
128
+ required: yes
129
+ ProjectNamePascalCase:
130
+ required: yes
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blufin-lib
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Albert Rannetsperger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-08 00:00:00.000000000 Z
11
+ date: 2019-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: kwalify
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.7.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.7.2
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: json
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -74,20 +88,24 @@ extra_rdoc_files: []
74
88
  files:
75
89
  - lib/blufin-lib.rb
76
90
  - lib/core/arrays.rb
91
+ - lib/core/base.rb
77
92
  - lib/core/browser.rb
93
+ - lib/core/config.rb
78
94
  - lib/core/datetime_utils.rb
79
95
  - lib/core/encryptor.rb
80
96
  - lib/core/files.rb
81
97
  - lib/core/image.rb
82
98
  - lib/core/network.rb
83
99
  - lib/core/numbers.rb
100
+ - lib/core/projects.rb
84
101
  - lib/core/routes.rb
85
- - lib/core/ssh.rb
86
102
  - lib/core/strings.rb
87
103
  - lib/core/terminal.rb
88
104
  - lib/core/tools.rb
89
105
  - lib/core/validate.rb
106
+ - lib/core/versioning.rb
90
107
  - lib/version.rb
108
+ - opt/awx/project-schema.yml
91
109
  homepage: https://github.com/alb3rtuk
92
110
  licenses:
93
111
  - MIT
@@ -1,145 +0,0 @@
1
- module Blufin
2
-
3
- class SSH
4
-
5
- SSH_PREFIX = 'ssh_'
6
-
7
- # SSH into remote host.
8
- # @return void
9
- def self.ssh_into_remote(config_key)
10
- if config_key.nil?
11
- show_available_ssh_hosts
12
- else
13
- ssh_user, ssh_host, ssh_cert = get_ssh_credentials(config_key)
14
- ssh_into_remote_credentials(ssh_user, ssh_host, ssh_cert)
15
- end
16
- end
17
-
18
- # SSH into remote with pre-supplied credentials.
19
- # @return void
20
- def self.ssh_into_remote_credentials(ssh_user, ssh_host, ssh_cert = nil)
21
- if ssh_cert.nil?
22
- Blufin::Terminal::command("ssh #{ssh_user}@#{ssh_host}", nil, false, false)
23
- else
24
- Blufin::Terminal::command("ssh -i #{ssh_cert} #{ssh_user}@#{ssh_host}", nil, false, false)
25
- end
26
- end
27
-
28
- # Source can be a file or directory but whatever it is, target must be the same (a file or directory).
29
- # @return void
30
- def self.scp_to_remote(config_key, source, target)
31
- if config_key.nil?
32
- show_available_ssh_hosts
33
- else
34
- ssh_user, ssh_host, ssh_cert = get_ssh_credentials(config_key)
35
- unless Blufin::Files::file_exists(source)
36
- Blufin::Terminal::error("The file doesn't exist: #{Blufin::Terminal::format_directory(source)}", nil, true)
37
- return
38
- end
39
- Blufin::Terminal::command("scp -i #{ssh_cert} #{source} #{ssh_user}@#{ssh_host}:#{target}", nil)
40
- end
41
- end
42
-
43
- private
44
-
45
- # Get SSH credentials from the config file.
46
- # @return Array
47
- def self.get_ssh_credentials(config_key)
48
- ssh_parts = Blufin::Config.get_custom_key(SSH_PREFIX, config_key).split('|')
49
- ssh_user = ssh_parts[0]
50
- ssh_host = ssh_parts[1]
51
- ssh_cert = ssh_parts[2].to_s.strip.length == 0 ? nil : ssh_parts[2]
52
- help_message = [
53
- "The file: #{Blufin::Terminal::format_directory(ConfigUnique::CONFIG_FILE)} must contain a line similar to the following:",
54
- nil,
55
- 'ssh_ec2=ec2-user|ec2-XX-XX-XX-XX.eu-west-1.compute.amazonaws.com|~/.ssh/pem-key.pem',
56
- 'ssh_ec2=ec2-user|ec2-XX-XX-XX-XX.eu-west-1.compute.amazonaws.com',
57
- ]
58
- Blufin::Terminal::error("SSH #{Blufin::Terminal::format_highlight('user')} required", help_message, true) if ssh_user.nil? || ssh_user == ''
59
- Blufin::Terminal::error("SSH #{Blufin::Terminal::format_highlight('host')} required", help_message, true) if ssh_host.nil? || ssh_host == ''
60
- unless ssh_cert.nil?
61
- unless Blufin::Files::file_exists(File.expand_path(ssh_cert))
62
- Blufin::Terminal::error("PEM key not found: #{Blufin::Terminal::format_directory(ssh_cert)}")
63
- end
64
- end
65
- return ssh_user, ssh_host, ssh_cert
66
- end
67
-
68
- # Check that SSHPASS is installed.
69
- # @return void
70
- def self.check_sshpass_is_installed
71
- Blufin::UtilsTools::check_sshpass_is_installed
72
- end
73
-
74
- # Runs a series of commands on the VM.
75
- # @return void
76
- def self.command(commands, ssh_user, ssh_host, ssh_cert, verbose = true)
77
- commands = [commands] unless commands.is_a?(Array)
78
- if commands.any?
79
- commands.each do |command|
80
- puts "\x1B[48;5;136m Executing \x1B[0m \xe2\x86\x92\x1B[0m #{Blufin::Terminal::format_command("#{command} \x1B[0m\xe2\x86\x92 \x1B[38;5;202m[\x1B[38;5;168m#{ssh_user}@#{ssh_host}\x1B[38;5;202m]\x1B[0m")}" if verbose
81
- if ssh_cert.nil?
82
- Blufin::Terminal::command("ssh #{ssh_user}@#{ssh_host} '#{command}'", nil, false, false)
83
- else
84
- Blufin::Terminal::command("ssh -i #{ssh_cert} #{ssh_user}@#{ssh_host} '#{command}'", nil, false, false)
85
- end
86
- end
87
- end
88
- end
89
-
90
- # Runs a series of commands on the VM and capture output.
91
- # Currently only works for single-line output (IE: Won't capture multi-line output properly).
92
- # @return void
93
- def self.command_capture(commands, ssh_user, ssh_host, ssh_cert, verbose = true)
94
- commands = [commands] unless commands.is_a?(Array)
95
- results = []
96
- if commands.any?
97
- commands.each do |command|
98
- puts "\x1B[48;5;136m Executing \x1B[0m \xe2\x86\x92\x1B[0m #{Blufin::Terminal::format_command("#{command} \x1B[0m\xe2\x86\x92 \x1B[38;5;202m[\x1B[38;5;168m#{ssh_user}@#{ssh_host}\x1B[38;5;202m]\x1B[0m")}" if verbose
99
- if ssh_cert.nil?
100
- results << Blufin::Terminal::command_capture("ssh #{ssh_user}@#{ssh_host} '#{command}'", nil, false, false)[0].gsub(/\n$/, '')
101
- else
102
- results << Blufin::Terminal::command_capture("ssh -i #{ssh_cert} #{ssh_user}@#{ssh_host} '#{command}'", nil, false, false)[0].gsub(/\n$/, '')
103
- end
104
- end
105
- end
106
- results
107
- end
108
-
109
- # Show a list of available SSH hosts.
110
- # @return void
111
- def self.show_available_ssh_hosts
112
- all_keys = Blufin::Config::params
113
- ssh_hosts = []
114
- ssh_hosts_lengths = []
115
- all_keys.keys.each do |key|
116
- if key =~ /\A#{SSH_PREFIX}.+/i
117
- ssh_hosts_lengths << key.length
118
- end
119
- end
120
- if ssh_hosts_lengths.any?
121
- ssh_hosts_lengths.uniq!
122
- max_key_length = ssh_hosts_lengths.max - SSH_PREFIX.length
123
- all_keys.keys.sort!.each do |key|
124
- if key =~ /\A#{SSH_PREFIX}.+/i
125
- ssh_user, ssh_host, ssh_cert = get_ssh_credentials(key.gsub(SSH_PREFIX, ''))
126
- ssh_hosts << "\x1B[38;5;154m#{key.gsub(SSH_PREFIX, '').rjust(max_key_length, ' ')}\x1B[0m\x1B[38;5;240m \xe2\x80\x94 #{ssh_host} \x1B[38;5;245m(#{ssh_user})#{ssh_cert.nil? ? '' : "\x1B[38;5;204m #{ssh_cert}"}"
127
- end
128
- end
129
- Blufin::Terminal::info('Pre-configured hosts to SSH into:', ssh_hosts)
130
- else
131
- help_message = [
132
- "To define a remote host \xe2\x80\x94 run #{Blufin::Terminal::format_command("#{ConfigUnique::GEM_NAME} x")} and add a line similar to:",
133
- nil,
134
- "\x1B[38;5;154mssh_ec2=ec2-user|ec2-XX-XX-XX-XX.eu-west-1.compute.amazonaws.com|~/.ssh/pem-key.pem",
135
- "\x1B[38;5;154mssh_ec2=ec2-user|ec2-XX-XX-XX-XX.eu-west-1.compute.amazonaws.com",
136
- nil,
137
- "IMPORTANT: Configuration key must start with prefix: #{Blufin::Terminal::format_highlight(SSH_PREFIX)}"
138
- ]
139
- Blufin::Terminal::info('No remote host(s) defined in configuration file', help_message)
140
- end
141
- end
142
-
143
- end
144
-
145
- end