orats 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|