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 +4 -4
- data/lib/blufin-lib.rb +4 -1
- data/lib/core/base.rb +17 -0
- data/lib/core/config.rb +110 -0
- data/lib/core/files.rb +17 -1
- data/lib/core/projects.rb +331 -0
- data/lib/core/strings.rb +11 -0
- data/lib/core/terminal.rb +85 -46
- data/lib/core/versioning.rb +29 -0
- data/lib/version.rb +1 -1
- data/opt/awx/project-schema.yml +130 -0
- metadata +21 -3
- data/lib/core/ssh.rb +0 -145
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 270bafc81b6eadf414cfadf2468ad3b85bf569b8
|
4
|
+
data.tar.gz: ef34b1af83e74ec125af126924d5772d8cadf6ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d95f777b6152f9b00ad9bd5ee0ac5894abd2a12f129927519656fd295f5af64d1681ff3e39d2c4d1f4c68bc9561def591c8fa198f7aae8688a4e31b5d482718d
|
7
|
+
data.tar.gz: 41b89f6e3318cc01fee5dca3b1d657fa199c39cb34f32e23446f89e812e7c55d7028874adf9d1ba446b5d0fd37d465c75e160b60db94735a8e559fb0c2449f03
|
data/lib/blufin-lib.rb
CHANGED
@@ -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
|
data/lib/core/base.rb
ADDED
@@ -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
|
data/lib/core/config.rb
ADDED
@@ -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
|
data/lib/core/files.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/core/strings.rb
CHANGED
@@ -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.
|
data/lib/core/terminal.rb
CHANGED
@@ -11,20 +11,20 @@ module Blufin
|
|
11
11
|
|
12
12
|
extend Columnist
|
13
13
|
|
14
|
-
MSG_INFO
|
15
|
-
MSG_WARNING
|
16
|
-
MSG_ERROR
|
17
|
-
MSG_TODO
|
18
|
-
MSG_AUTOMATIC
|
19
|
-
MSG_GENERATED
|
20
|
-
MSG_PROCESSED
|
21
|
-
MSG_PROGRESS
|
22
|
-
MSG_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[
|
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[
|
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
|
76
|
-
|
77
|
-
|
78
|
-
spinner.
|
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 =
|
87
|
+
res = `cd #{path} && #{command}`
|
82
88
|
else
|
83
89
|
res = system("cd #{path} && #{command} > /tmp/execute-output")
|
84
90
|
end
|
85
|
-
t2
|
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
|
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
|
108
|
-
t2
|
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
|
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;
|
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
|
299
|
+
return "\x1B[38;5;#{color}m#{highlighted_text.upcase}#{color_end}"
|
278
300
|
else
|
279
|
-
return "\x1B[38;5
|
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
|
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;
|
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;
|
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;
|
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
|
-
|
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
|
458
|
-
found_name
|
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
|
525
|
-
limit
|
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
|
528
|
-
file_more
|
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
|
596
|
+
type = type.downcase
|
559
597
|
types = {
|
560
|
-
'yml'
|
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
|
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
|
data/lib/version.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
BLUFIN_LIB_VERSION = '1.5.
|
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.
|
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-
|
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
|
data/lib/core/ssh.rb
DELETED
@@ -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
|