orats 0.5.1 → 0.6.0
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/README.md +11 -4
- data/lib/orats/cli.rb +34 -23
- data/lib/orats/commands/common.rb +82 -0
- data/lib/orats/commands/new/ansible.rb +105 -0
- data/lib/orats/commands/new/exec.rb +54 -0
- data/lib/orats/commands/new/foreman.rb +55 -0
- data/lib/orats/commands/new/rails.rb +107 -0
- data/lib/orats/commands/nuke.rb +72 -0
- data/lib/orats/commands/outdated/compare.rb +100 -0
- data/lib/orats/commands/outdated/exec.rb +46 -0
- data/lib/orats/commands/outdated/parse.rb +62 -0
- data/lib/orats/commands/play.rb +34 -0
- data/lib/orats/commands/ui.rb +52 -0
- data/lib/orats/templates/auth.rb +1 -3
- data/lib/orats/templates/base.rb +243 -11
- data/lib/orats/templates/includes/Galaxyfile +1 -1
- data/lib/orats/templates/includes/Gemfile +5 -1
- data/lib/orats/templates/includes/inventory/group_vars/all.yml +14 -9
- data/lib/orats/templates/play.rb +0 -3
- data/lib/orats/version.rb +1 -1
- metadata +13 -6
- data/lib/orats/command.rb +0 -107
- data/lib/orats/foreman.rb +0 -54
- data/lib/orats/shell.rb +0 -503
@@ -0,0 +1,107 @@
|
|
1
|
+
module Orats
|
2
|
+
module Commands
|
3
|
+
module New
|
4
|
+
module Rails
|
5
|
+
def rails_template(command, flags = '')
|
6
|
+
exit_if_cannot_rails
|
7
|
+
exit_if_exists unless flags.index(/--skip/)
|
8
|
+
|
9
|
+
orats_template = "--template #{base_path}/templates/#{command}.rb"
|
10
|
+
|
11
|
+
run "rails new #{@active_path} #{flags} --skip-bundle #{orats_template unless command.empty?}"
|
12
|
+
yield if block_given?
|
13
|
+
end
|
14
|
+
|
15
|
+
def custom_rails_template
|
16
|
+
log_thor_task 'shell', 'Running custom rails template'
|
17
|
+
|
18
|
+
@options[:template].include?('://') ? url_to_string(@options[:template])
|
19
|
+
: file_to_string(@options[:template])
|
20
|
+
|
21
|
+
rails_template '', "--skip --template #{@options[:template]}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def gsub_postgres_info
|
25
|
+
log_thor_task 'root', 'Changing the postgres information'
|
26
|
+
gsub_file "#{@active_path}/.env", 'DATABASE_HOST: localhost', "DATABASE_HOST: #{@options[:pg_location]}"
|
27
|
+
gsub_file "#{@active_path}/.env", ': postgres', ": #{@options[:pg_username]}"
|
28
|
+
gsub_file "#{@active_path}/.env", ': supersecrets', ": #{@options[:pg_password]}"
|
29
|
+
|
30
|
+
git_commit 'Change the postgres information'
|
31
|
+
end
|
32
|
+
|
33
|
+
def gsub_redis_info
|
34
|
+
log_thor_task 'root', 'Adding the redis password'
|
35
|
+
gsub_file "#{@active_path}/config/initializers/sidekiq.rb", '//', "//:#{ENV['CACHE_PASSWORD']}@"
|
36
|
+
gsub_file "#{@active_path}/.env", 'HE_PASSWORD: ', "HE_PASSWORD: #{@options[:redis_password]}"
|
37
|
+
gsub_file "#{@active_path}/.env", 'CACHE_HOST: localhost', "CACHE_HOST: #{@options[:redis_location]}"
|
38
|
+
gsub_file "#{@active_path}/config/application.rb", '# pass', 'pass'
|
39
|
+
|
40
|
+
git_commit 'Add the redis password'
|
41
|
+
end
|
42
|
+
|
43
|
+
def gsub_project_path
|
44
|
+
log_thor_task 'root', 'Changing the project path'
|
45
|
+
gsub_file "#{@active_path}/.env", ': /full/path/to/your/project', ": #{File.expand_path(@active_path)}"
|
46
|
+
|
47
|
+
git_commit 'Add the development project path'
|
48
|
+
end
|
49
|
+
|
50
|
+
def bundle_install
|
51
|
+
log_thor_task 'shell', 'Running bundle install, this may take a while'
|
52
|
+
run_from @active_path, 'bundle install'
|
53
|
+
|
54
|
+
git_commit 'Add gem lock file'
|
55
|
+
end
|
56
|
+
|
57
|
+
def bundle_binstubs
|
58
|
+
log_thor_task 'shell', 'Running bundle binstubs for a few gems'
|
59
|
+
run_from @active_path, 'bundle binstubs whenever puma sidekiq backup'
|
60
|
+
|
61
|
+
git_commit 'Add binstubs for the important gems'
|
62
|
+
end
|
63
|
+
|
64
|
+
def spring_binstub
|
65
|
+
log_thor_task 'shell', 'Running spring binstub'
|
66
|
+
run_from @active_path, 'bundle exec spring binstub --all'
|
67
|
+
|
68
|
+
git_commit 'Springify all of the bins'
|
69
|
+
end
|
70
|
+
|
71
|
+
def run_rake(command)
|
72
|
+
log_thor_task 'shell', 'Running rake commands'
|
73
|
+
|
74
|
+
run_from @active_path, "bundle exec rake #{command}"
|
75
|
+
end
|
76
|
+
|
77
|
+
def create_and_migrate_database
|
78
|
+
run_rake 'db:create:all db:migrate'
|
79
|
+
git_commit 'Add the database schema file'
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def exit_if_cannot_rails
|
85
|
+
log_thor_task 'shell', 'Checking for rails'
|
86
|
+
|
87
|
+
has_rails = run('which rails', capture: true)
|
88
|
+
|
89
|
+
log_error 'error', 'Cannot access rails', 'question', 'Are you sure you have rails setup correctly?', true do
|
90
|
+
log_status_bottom 'tip', 'You can install it by running `gem install rails`', :white
|
91
|
+
end if has_rails.empty?
|
92
|
+
|
93
|
+
exit 1 if has_rails.empty?
|
94
|
+
end
|
95
|
+
|
96
|
+
def exit_if_exists
|
97
|
+
log_thor_task 'shell', 'Checking if a file or directory already exists'
|
98
|
+
|
99
|
+
if Dir.exist?(@active_path) || File.exist?(@active_path)
|
100
|
+
log_error 'error', 'A file or directory already exists at this location', 'path', @active_path
|
101
|
+
exit 1
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'orats/commands/common'
|
2
|
+
|
3
|
+
module Orats
|
4
|
+
module Commands
|
5
|
+
class Nuke < Common
|
6
|
+
def initialize(target_path = '', options = {})
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def init
|
11
|
+
log_nuke_info
|
12
|
+
log_nuke_details unless @options[:skip_data]
|
13
|
+
|
14
|
+
confirmed_to_delete = yes?('Are you sure? (y/N)', :cyan); puts
|
15
|
+
|
16
|
+
if confirmed_to_delete
|
17
|
+
nuke_data unless @options[:skip_data]
|
18
|
+
nuke_directory
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def log_nuke_info
|
25
|
+
log_error 'nuke', 'You are about to permanently delete this directory',
|
26
|
+
'path', File.expand_path(@target_path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def log_nuke_details
|
30
|
+
rails_projects = []
|
31
|
+
|
32
|
+
valid_rails_directories.each { |rails_dir| rails_projects << File.basename(rails_dir) }
|
33
|
+
project_names = rails_projects.join(', ')
|
34
|
+
|
35
|
+
log_error 'nuke', 'You are about to permanently delete all postgres databases for',
|
36
|
+
'databases', project_names, true
|
37
|
+
|
38
|
+
log_error 'nuke', 'You are about to permanently delete all redis namespaces for',
|
39
|
+
'namespaces', project_names
|
40
|
+
end
|
41
|
+
|
42
|
+
def valid_rails_directories
|
43
|
+
rails_gemfiles =
|
44
|
+
run("find #{@active_path} -type f -name Gemfile | xargs grep -lE \"gem 'rails'|gem \\\"rails\\\"\"",
|
45
|
+
capture: true)
|
46
|
+
|
47
|
+
gemfile_paths = rails_gemfiles.split("\n")
|
48
|
+
|
49
|
+
gemfile_paths.map { |gemfile| File.dirname(gemfile) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def nuke_data
|
53
|
+
valid_rails_directories.each do |directory|
|
54
|
+
log_thor_task 'root', 'Removing postgres databases'
|
55
|
+
run_from directory, 'bundle exec rake db:drop:all'
|
56
|
+
|
57
|
+
nuke_redis File.basename(directory)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def nuke_redis(namespace)
|
62
|
+
log_thor_task 'root', 'Removing redis keys'
|
63
|
+
run "redis-cli KEYS '#{namespace}:*' | xargs --delim='\n' redis-cli DEL"
|
64
|
+
end
|
65
|
+
|
66
|
+
def nuke_directory
|
67
|
+
log_thor_task 'root', 'Deleting directory'
|
68
|
+
run "rm -rf #{@active_path}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module Orats
|
2
|
+
module Commands
|
3
|
+
module Outdated
|
4
|
+
module Compare
|
5
|
+
def remote_to_local_gem_versions
|
6
|
+
log_remote_info 'gem', 'Comparing this version of orats to the latest orats version',
|
7
|
+
'version', "Latest: #{@remote_gem_version}, Yours: v#{VERSION}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def remote_to_local_galaxyfiles
|
11
|
+
galaxyfile_diff = @remote_galaxyfile - @local_galaxyfile
|
12
|
+
local_galaxyfile_as_string = @local_galaxyfile.join
|
13
|
+
local_galaxyfile_roles = @local_galaxyfile.size
|
14
|
+
roles_diff_count = galaxyfile_diff.size
|
15
|
+
|
16
|
+
log_status_top 'roles', "Comparing this version of orats' roles to the latest version:", :green
|
17
|
+
|
18
|
+
if roles_diff_count == 0
|
19
|
+
log_status_bottom 'message', "All #{local_galaxyfile_roles} roles are up to date", :yellow
|
20
|
+
else
|
21
|
+
log_status_bottom 'message', "There are #{roles_diff_count} differences", :yellow
|
22
|
+
|
23
|
+
galaxyfile_diff.each do |line|
|
24
|
+
name = line.split(',').first
|
25
|
+
status = 'outdated'
|
26
|
+
color = :yellow
|
27
|
+
|
28
|
+
unless local_galaxyfile_as_string.include?(name)
|
29
|
+
status = 'missing'
|
30
|
+
color = :red
|
31
|
+
end
|
32
|
+
|
33
|
+
log_status_bottom status, name, color, true
|
34
|
+
end
|
35
|
+
|
36
|
+
log_results 'The latest version of orats may benefit you', 'Check github to see if the changes interest you'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def remote_to_local(label, keyword, remote, local)
|
41
|
+
item_diff = remote - local
|
42
|
+
item_diff_count = item_diff.size
|
43
|
+
|
44
|
+
log_remote_info label, "Comparing this version of orats' #{label} to the latest version",
|
45
|
+
'file', label == 'playbook' ? 'site.yml' : 'all.yml'
|
46
|
+
|
47
|
+
item_diff.each do |line|
|
48
|
+
log_status_bottom 'missing', line, :red, true unless local.include?(line)
|
49
|
+
end
|
50
|
+
|
51
|
+
if item_diff_count > 0
|
52
|
+
log_results "#{item_diff_count} new #{keyword} are available",
|
53
|
+
'You may benefit from upgrading to the latest orats'
|
54
|
+
else
|
55
|
+
log_results 'Everything appears to be in order', "No missing #{keyword} were found"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def local_to_user(label, keyword, flag_path, local)
|
60
|
+
user = yield
|
61
|
+
|
62
|
+
log_local_info label, "Comparing this version of orats' #{label} to #{File.basename(flag_path)}",
|
63
|
+
'path', flag_path
|
64
|
+
|
65
|
+
missing_count = log_unmatched(local, user, 'missing', :red)
|
66
|
+
extra_count = log_unmatched(user, local, 'extra', :yellow)
|
67
|
+
|
68
|
+
if missing_count > 0
|
69
|
+
log_results "#{missing_count} #{keyword} are missing",
|
70
|
+
"Your ansible run will likely fail with this #{label}"
|
71
|
+
else
|
72
|
+
log_results 'Everything appears to be in order', "No missing #{keyword} were found"
|
73
|
+
end
|
74
|
+
|
75
|
+
if extra_count > 0
|
76
|
+
log_results "#{extra_count} extra #{keyword} were detected:",
|
77
|
+
"No problem but remember to add them to future #{keyword}"
|
78
|
+
else
|
79
|
+
log_results "No extra #{keyword} were found:", "Extra #{keyword} are fine but you have none"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def log_unmatched(compare, against, label, color)
|
86
|
+
count = 0
|
87
|
+
|
88
|
+
compare.each do |item|
|
89
|
+
unless against.include?(item)
|
90
|
+
log_status_bottom label, item, color, true
|
91
|
+
count += 1
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
count
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'orats/commands/common'
|
2
|
+
require 'orats/version'
|
3
|
+
require 'orats/commands/outdated/parse'
|
4
|
+
require 'orats/commands/outdated/compare'
|
5
|
+
|
6
|
+
module Orats
|
7
|
+
module Commands
|
8
|
+
module Outdated
|
9
|
+
class Exec < Commands::Common
|
10
|
+
include Parse
|
11
|
+
include Compare
|
12
|
+
|
13
|
+
def initialize(target_path = '', options = {})
|
14
|
+
super
|
15
|
+
|
16
|
+
@remote_galaxyfile = galaxyfile url_to_string(@remote_paths[:galaxyfile])
|
17
|
+
@remote_inventory = inventory url_to_string(@remote_paths[:inventory])
|
18
|
+
@remote_playbook = playbook url_to_string(@remote_paths[:playbook])
|
19
|
+
|
20
|
+
@local_galaxyfile = galaxyfile file_to_string(@local_paths[:galaxyfile])
|
21
|
+
@local_inventory = inventory file_to_string(@local_paths[:inventory])
|
22
|
+
@local_playbook = playbook file_to_string(@local_paths[:playbook])
|
23
|
+
end
|
24
|
+
|
25
|
+
def init
|
26
|
+
remote_to_local_gem_versions
|
27
|
+
remote_to_local_galaxyfiles
|
28
|
+
remote_to_local 'inventory', 'variables', @remote_inventory, @local_inventory
|
29
|
+
remote_to_local 'playbook', 'roles', @remote_playbook, @local_playbook
|
30
|
+
|
31
|
+
unless @options[:playbook].empty?
|
32
|
+
local_to_user('playbook', 'roles', @options[:playbook], @local_playbook) do
|
33
|
+
playbook file_to_string(@options[:playbook])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
unless @options[:inventory].empty?
|
38
|
+
local_to_user('inventory', 'variables', @options[:inventory], @local_inventory) do
|
39
|
+
inventory file_to_string(@options[:inventory])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Orats
|
2
|
+
module Commands
|
3
|
+
module Outdated
|
4
|
+
module Parse
|
5
|
+
def gem_version
|
6
|
+
"v#{url_to_string(@remote_paths[:version]).match(/'(.*)'/)[1..-1].first}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def galaxyfile(contents)
|
10
|
+
contents.split
|
11
|
+
end
|
12
|
+
|
13
|
+
def inventory(contents)
|
14
|
+
# pluck out all of the values contained with {{ }}
|
15
|
+
ansible_variables = contents.scan(/\{\{([^{{}}]*)\}\}/)
|
16
|
+
|
17
|
+
# remove the leading space
|
18
|
+
ansible_variables.map! { |line| line.first[0] = '' }
|
19
|
+
|
20
|
+
# match every line that is not a comment and contains a colon
|
21
|
+
inventory_variables = contents.scan(/^[^#].*:/)
|
22
|
+
|
23
|
+
inventory_variables.map! do |line|
|
24
|
+
# only strip lines that need it
|
25
|
+
line.strip! if line.include?(' ') || line.include?("\n")
|
26
|
+
|
27
|
+
# get rid of the trailing colon
|
28
|
+
line.chomp(':')
|
29
|
+
|
30
|
+
# if a value of a certain variable has a colon then the regex
|
31
|
+
# picks this up as a match. only take the variable name
|
32
|
+
# if this happens to occur
|
33
|
+
line.split(':').first if line.include?(':')
|
34
|
+
end
|
35
|
+
|
36
|
+
(ansible_variables + inventory_variables).uniq.delete_if(&:empty?)
|
37
|
+
end
|
38
|
+
|
39
|
+
def playbook(contents)
|
40
|
+
roles = contents.scan(/^.*role:.*/)
|
41
|
+
|
42
|
+
roles.map! do |line|
|
43
|
+
line.strip! if line.include?(' ') || line.include?("\n")
|
44
|
+
|
45
|
+
role_parts = line.split('role:')
|
46
|
+
|
47
|
+
# start at the actual role name
|
48
|
+
line = role_parts[1]
|
49
|
+
|
50
|
+
if line.include?(',')
|
51
|
+
line = line.split(',').first
|
52
|
+
end
|
53
|
+
|
54
|
+
line.strip! if line.include?(' ')
|
55
|
+
end
|
56
|
+
|
57
|
+
roles.uniq
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'orats/commands/common'
|
2
|
+
require 'orats/commands/new/rails'
|
3
|
+
|
4
|
+
module Orats
|
5
|
+
module Commands
|
6
|
+
class Play < Common
|
7
|
+
include New::Rails
|
8
|
+
|
9
|
+
def initialize(target_path = '', options = {})
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def init
|
14
|
+
return unless can_play?
|
15
|
+
rails_template 'play'
|
16
|
+
custom_rails_template unless @options[:template].empty?
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def can_play?
|
22
|
+
log_thor_task 'shell', 'Checking for the ansible binary'
|
23
|
+
|
24
|
+
has_ansible = run('which ansible', capture: true)
|
25
|
+
|
26
|
+
log_error 'error', 'Cannot access ansible', 'question', 'Are you sure you have ansible setup correctly?', true do
|
27
|
+
log_status_bottom 'tip', 'http://docs.ansible.com/intro_installation.html', :white
|
28
|
+
end if has_ansible.empty?
|
29
|
+
|
30
|
+
!has_ansible.empty?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Orats
|
2
|
+
module Commands
|
3
|
+
module UI
|
4
|
+
include Thor::Shell
|
5
|
+
|
6
|
+
def log_thor_task(type, message)
|
7
|
+
puts
|
8
|
+
say_status type, "#{message}...", :yellow
|
9
|
+
puts '-'*80, ''; sleep 0.25
|
10
|
+
end
|
11
|
+
|
12
|
+
def log_status_top(type, message, color)
|
13
|
+
puts
|
14
|
+
say_status type, set_color(message, :bold), color
|
15
|
+
end
|
16
|
+
|
17
|
+
def log_status_bottom(type, message, color, strip_newline = false)
|
18
|
+
say_status type, message, color
|
19
|
+
puts unless strip_newline
|
20
|
+
end
|
21
|
+
|
22
|
+
def log_results(results, message)
|
23
|
+
log_status_top 'results', "#{results}:", :magenta
|
24
|
+
log_status_bottom 'message', message, :white
|
25
|
+
end
|
26
|
+
|
27
|
+
def log_error(top_label, top_message, bottom_label, bottom_message, strip_newline = false)
|
28
|
+
log_status_top top_label, "#{top_message}:", :red
|
29
|
+
log_status_bottom bottom_label, bottom_message , :yellow, strip_newline
|
30
|
+
yield if block_given?
|
31
|
+
end
|
32
|
+
|
33
|
+
def log_remote_info(top_label, top_message, bottom_label, bottom_message)
|
34
|
+
log_status_top top_label, "#{top_message}:", :green
|
35
|
+
log_status_bottom bottom_label, bottom_message, :yellow
|
36
|
+
end
|
37
|
+
|
38
|
+
def log_local_info(top_label, top_message, bottom_label, bottom_message)
|
39
|
+
log_status_top top_label, "#{top_message}:", :blue
|
40
|
+
log_status_bottom bottom_label, bottom_message, :cyan
|
41
|
+
end
|
42
|
+
|
43
|
+
def run_from(path, command)
|
44
|
+
run "cd #{path} && #{command} && cd -"
|
45
|
+
end
|
46
|
+
|
47
|
+
def git_commit(message)
|
48
|
+
run_from @active_path, "git add -A && git commit -m '#{message}'"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|