blufin-lib 1.5.0 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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