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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 86701c9d3879d19d5be1fdae7ba6322db003c08f
4
- data.tar.gz: 2eca54c7c2359e3c71da893813cd61360f484951
3
+ metadata.gz: ed2be672732a97ee0adf810bdef6be8e40df6415
4
+ data.tar.gz: caa247df80d369533b424a261ba57b254501b579
5
5
  SHA512:
6
- metadata.gz: a8ee825ee47eaa23e1f240280f0d023b06c1256cb858d2c577acd822d5a744858cb5c26c2e2ec0542934aeafc5da6c8ce6096e6e1b4b373f0aa3f9f56255f94c
7
- data.tar.gz: 93721d778eb7063a2b393735fbfd101d55841116972d6f72583acf0aa6b0c33dc7b229a92458abf69a0bb93053f72f8a53c396dd61aafd56a4c4326a508bf57a
6
+ metadata.gz: 4f26035196b05988cd3eda4a894cbd651aedb8a33a6da75eae97fb5e0d43b1df49e066945f928b2b8e7b1b21a9df1d005dbf926688ae40ecce4b70e19f81f86f
7
+ data.tar.gz: 67cbd89f2b3eb6a93e1a099e7b5b910f05c288a1d8f811fdbf8f8297af40727ed738ad6b18e2acf7f7509609c8f2a8a5415e33326b9297004d973385969bf2ae
data/README.md CHANGED
@@ -6,6 +6,8 @@ It stands for opinionated rails application templates. The templates include sol
6
6
  projects. It handles creating a rails application with a bunch of opinions and optionally an ansible playbook so you can
7
7
  deploy your apps quickly.
8
8
 
9
+ You can also optionally include custom rails templates to append to any template you create with orats.
10
+
9
11
  Everything is accessed through the [orats gem](#installation).
10
12
 
11
13
  ## What version of Rails and Ruby are you targeting?
@@ -62,7 +64,7 @@ Here is an overview of the available commands. You can find out more information
62
64
  running `orats <command name> help` from your terminal. You can also type `orats` on its own to see a list of all commands.
63
65
 
64
66
  - Create a new orats project
65
- - `orats new <APP_PATH> --pg-password <development postgres db password>`
67
+ - `orats new <TARGET_PATH> --pg-password <development postgres db password>`
66
68
  - Configuration:
67
69
  - Optionally takes: `--pg-location [localhost]`
68
70
  - Optionally takes: `--pg-username [postgres]`
@@ -70,6 +72,7 @@ running `orats <command name> help` from your terminal. You can also type `orats
70
72
  - Optionally takes: `--redis-password []`
71
73
  - Template features:
72
74
  - Optionally takes: `--auth [false]`
75
+ - Optionally takes: `--template []`
73
76
  - Project features:
74
77
  - Optionally takes: `--skip-extras [false]`
75
78
  - Optionally takes: `--skip-foreman-start [false]`
@@ -78,10 +81,12 @@ running `orats <command name> help` from your terminal. You can also type `orats
78
81
  - Optionally takes: `--skip-galaxy [false]`
79
82
 
80
83
  - Create an ansible playbook
81
- - `orats play <PATH>`
84
+ - `orats play <TARGET_PATH>`
85
+ - Template features:
86
+ - Optionally takes: `--template []`
82
87
 
83
88
  - Delete the directory and optionally all data associated to it
84
- - `orats nuke <APP_PATH>`
89
+ - `orats nuke <TARGET_PATH>`
85
90
  - Optionally takes: `--skip-data [false]`
86
91
 
87
92
  - Detect whether or not orats, the playbook or inventory is outdated
@@ -124,10 +129,12 @@ it includes these features and when I do not want a specific thing it is much qu
124
129
  - Create development, staging and production environments.
125
130
  - Use environment variables for things that are likely to change per environment.
126
131
  - Use environment variables for anything that is sensitive and should not be included into version control.
132
+ - Add environment variables for google analytics UI, disqus short name and S3 in addition to a bunch of typical rails values.
127
133
  - Use redis as the cache backend.
128
134
  - Use sidekiq as a background worker.
129
135
  - Use puma as the server with settings capable of doing phased restarts.
130
136
  - Use foreman in development mode to manage starting both the rails server using puma and sidekiq.
137
+ - Add a rake task to handle upgrades using the `backup` gem.
131
138
  - Set the production asset precompiler to include fonts and png files.
132
139
  - Set the production logger to rotate the logs daily.
133
140
  - Set the timezone to EST.
@@ -140,7 +147,7 @@ it includes these features and when I do not want a specific thing it is much qu
140
147
  - Add 3 view helpers to easily set a page's title, meta description and page heading. All of which are optional.
141
148
  - Bootstrap ~3 layout file with conditionally loaded `html5shiv`, `json3` and `respondjs` libs for IE < 9 support.
142
149
  - Separate the navigation, navigation links, flash messages and footer partials.
143
- - Add partials and environment variables for both google analytics and disqus.
150
+ - Add partials for both google analytics and disqus.
144
151
  - Public 404, 422, 500 and 502 pages so they can be served directly from your web server.
145
152
  - Use sass and coffeescript.
146
153
  - jquery 1.10.x loaded through a CDN.
@@ -1,21 +1,25 @@
1
1
  require 'thor'
2
- require 'orats/command'
2
+ require 'orats/commands/new/exec'
3
+ require 'orats/commands/outdated/exec'
4
+ require 'orats/commands/play'
5
+ require 'orats/commands/nuke'
3
6
 
4
7
  module Orats
5
8
  class CLI < Thor
6
- option :pg_location, default: 'localhost'
7
- option :pg_username, default: 'postgres'
8
- option :pg_password, required: true
9
- option :redis_location, default: 'localhost'
10
- option :redis_password, default: ''
9
+ option :pg_location, default: 'localhost', aliases: '-l'
10
+ option :pg_username, default: 'postgres', aliases: '-u'
11
+ option :pg_password, required: true, aliases: '-p'
12
+ option :redis_location, default: 'localhost', aliases: '-n'
13
+ option :redis_password, default: '', aliases: '-d'
11
14
  option :auth, type: :boolean, default: false, aliases: '-a'
15
+ option :template, default: '', aliases: '-m'
12
16
  option :skip_extras, type: :boolean, default: false, aliases: '-E'
13
17
  option :skip_foreman_start, type: :boolean, default: false, aliases: '-F'
14
- option :sudo_password, default: ''
18
+ option :sudo_password, default: '', aliases: '-s'
15
19
  option :skip_galaxy, type: :boolean, default: false, aliases: '-G'
16
- desc 'new APP_PATH [options]', ''
20
+ desc 'new TARGET_PATH [options]', ''
17
21
  long_desc <<-D
18
- `orats new myapp --pg-password supersecret` will create a new rails project and it will also create an ansible inventory to go with it by default.
22
+ `orats new target_path --pg-password supersecret` will create a new rails project and it will also create an ansible inventory to go with it by default.
19
23
 
20
24
  You must supply at least this flag:
21
25
 
@@ -35,6 +39,8 @@ module Orats
35
39
 
36
40
  `--auth` will include authentication and authorization [false]
37
41
 
42
+ `--template` will let you supply a custom template, a url or file is ok but urls must start with http or https []
43
+
38
44
  Project features:
39
45
 
40
46
  `--skip-extras` skip creating the services directory and ansible inventory/secrets [false]
@@ -47,33 +53,38 @@ module Orats
47
53
 
48
54
  `--skip-galaxy` skip automatically installing roles from the galaxy [false]
49
55
  D
50
- def new(app_name)
51
- Command.new(app_name, options).new
56
+ def new(target_path)
57
+ Commands::New::Exec.new(target_path, options).init
52
58
  end
53
59
 
54
- desc 'play PATH', ''
60
+ option :template, default: '', aliases: '-m'
61
+ desc 'play PATH [options]', ''
55
62
  long_desc <<-D
56
- `orats play path` will create an ansible playbook.
63
+ `orats play target_path` will create an ansible playbook.
64
+
65
+ Template features:
66
+
67
+ `--template` will let you supply a custom template, a url or file is ok but urls must start with http or https []
57
68
  D
58
- def play(app_name)
59
- Command.new(app_name).play
69
+ def play(target_path)
70
+ Commands::Play.new(target_path, options).init
60
71
  end
61
72
 
62
73
  option :skip_data, type: :boolean, default: false, aliases: '-D'
63
- desc 'nuke APP_PATH [options]', ''
74
+ desc 'nuke TARGET_PATH [options]', ''
64
75
  long_desc <<-D
65
- `orats nuke myapp` will delete the directory and optionally all data associated to it.
76
+ `orats nuke target_path` will delete the directory and optionally all data associated to it.
66
77
 
67
78
  Options:
68
79
 
69
80
  `--skip-data` will skip deleting app specific postgres databases and redis namespaces [false]
70
81
  D
71
- def nuke(app_name)
72
- Command.new(app_name, options).nuke
82
+ def nuke(target_path)
83
+ Commands::Nuke.new(target_path, options).init
73
84
  end
74
85
 
75
- option :playbook, default: ''
76
- option :inventory, default: ''
86
+ option :playbook, default: '', aliases: '-p'
87
+ option :inventory, default: '', aliases: '-i'
77
88
  desc 'outdated [options]', ''
78
89
  long_desc <<-D
79
90
  `orats outdated` will run various comparisons on orats and your ansible files.
@@ -91,7 +102,7 @@ module Orats
91
102
  `--inventory` to supply an inventory file for comparison []
92
103
  D
93
104
  def outdated
94
- Command.new(nil, options).outdated
105
+ Commands::Outdated::Exec.new(nil, options).init
95
106
  end
96
107
 
97
108
  desc 'version', ''
@@ -99,7 +110,7 @@ module Orats
99
110
  `orats version` will print the current version.
100
111
  D
101
112
  def version
102
- Command.new.version
113
+ puts "Orats version #{VERSION}"
103
114
  end
104
115
  map %w(-v --version) => :version
105
116
 
@@ -0,0 +1,82 @@
1
+ require 'orats/commands/ui'
2
+ require 'orats/commands/outdated/parse'
3
+
4
+ module Orats
5
+ module Commands
6
+ class Common
7
+ include Thor::Base
8
+ include Thor::Shell
9
+ include Thor::Actions
10
+ include UI
11
+ include Outdated::Parse
12
+
13
+ RELATIVE_PATHS = {
14
+ galaxyfile: 'templates/includes/Galaxyfile',
15
+ hosts: 'templates/includes/inventory/hosts',
16
+ inventory: 'templates/includes/inventory/group_vars/all.yml',
17
+ playbook: 'templates/play.rb',
18
+ version: 'version.rb'
19
+ }
20
+
21
+ attr_accessor :remote_gem_version, :remote_paths, :local_paths
22
+
23
+ def initialize(target_path = '', options = {})
24
+ @target_path = target_path
25
+ @options = options
26
+ @active_path = @target_path
27
+
28
+ @local_paths = {}
29
+ @remote_paths = {}
30
+
31
+ build_common_paths
32
+
33
+ self.destination_root = Dir.pwd
34
+ @behavior = :invoke
35
+ end
36
+
37
+ private
38
+
39
+ def base_path
40
+ File.join(File.expand_path(File.dirname(__FILE__)), '..')
41
+ end
42
+
43
+ def repo_path
44
+ %w(https://raw.githubusercontent.com/nickjj/orats lib/orats)
45
+ end
46
+
47
+ def select_branch(branch, value)
48
+ "#{repo_path[0]}/#{branch}/#{repo_path[1]}/#{value}"
49
+ end
50
+
51
+ def build_common_paths
52
+ @remote_paths[:version] = select_branch 'master', RELATIVE_PATHS[:version]
53
+ @remote_gem_version = gem_version
54
+
55
+ RELATIVE_PATHS.each_pair do |key, value|
56
+ @local_paths[key] = "#{base_path}/#{value}"
57
+ @remote_paths[key] = select_branch @remote_gem_version, value
58
+ end
59
+ end
60
+
61
+ def url_to_string(url)
62
+ begin
63
+ open(url).read
64
+ rescue *[OpenURI::HTTPError, SocketError] => ex
65
+ log_error 'error', "Error accessing URL #{url}",
66
+ 'message', ex
67
+ exit 1
68
+ end
69
+ end
70
+
71
+ def file_to_string(path)
72
+ if File.exist?(path) && File.file?(path)
73
+ IO.read(path)
74
+ else
75
+ log_error 'error', 'Error finding file',
76
+ 'message', path
77
+ exit 1
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,105 @@
1
+ require 'securerandom'
2
+
3
+ module Orats
4
+ module Commands
5
+ module New
6
+ module Ansible
7
+ def ansible_extras
8
+ create_inventory
9
+
10
+ secrets_path = "#{@target_path}/secrets"
11
+ create_secrets secrets_path
12
+
13
+ log_thor_task 'shell', 'Modifying secrets path in group_vars/all.yml'
14
+ gsub_file "#{@target_path}/#{fix_path_for_user(Commands::Common::RELATIVE_PATHS[:inventory])}",
15
+ '~/tmp/testproj/secrets/', File.expand_path(secrets_path)
16
+
17
+ log_thor_task 'shell', 'Modifying the place holder app name in group_vars/all.yml'
18
+ gsub_file "#{@target_path}/#{fix_path_for_user(Commands::Common::RELATIVE_PATHS[:inventory])}",
19
+ 'testproj', File.basename(@target_path)
20
+
21
+ log_thor_task 'shell', 'Creating ssh keypair'
22
+ run "ssh-keygen -t rsa -P '' -f #{secrets_path}/id_rsa"
23
+
24
+ log_thor_task 'shell', 'Creating self signed ssl certificates'
25
+ run create_rsa_certificate(secrets_path, 'sslkey.key', 'sslcert.crt')
26
+
27
+ log_thor_task 'shell', 'Creating monit pem file'
28
+ run "#{create_rsa_certificate(secrets_path,
29
+ 'monit.pem', 'monit.pem')} && openssl gendh 512 >> #{secrets_path}/monit.pem"
30
+
31
+ install_role_dependencies unless @options[:skip_galaxy]
32
+ end
33
+
34
+ private
35
+
36
+ def create_inventory
37
+ log_thor_task 'shell', 'Creating ansible inventory'
38
+ run "mkdir -p #{@target_path}/inventory/group_vars"
39
+
40
+ local_to_user Commands::Common::RELATIVE_PATHS[:hosts]
41
+ local_to_user Commands::Common::RELATIVE_PATHS[:inventory]
42
+ end
43
+
44
+ def local_to_user(file)
45
+ fixed_file = fix_path_for_user(file)
46
+
47
+ log_thor_task 'shell', "Creating #{fixed_file}"
48
+ run "cp #{base_path}/#{file} #{@target_path}/#{fixed_file}"
49
+ end
50
+
51
+ def create_secrets(secrets_path)
52
+ log_thor_task 'shell', 'Creating ansible secrets'
53
+ run "mkdir #{secrets_path}"
54
+
55
+ if @options[:redis_password].empty?
56
+ run "touch #{secrets_path}/redis_password"
57
+ else
58
+ save_secret_string "#{secrets_path}/redis_password"
59
+ gsub_file "#{@target_path}/#{fix_path_for_user(Commands::Common::RELATIVE_PATHS[:inventory])}",
60
+ 'redis_password: false', 'redis_password: true'
61
+ end
62
+
63
+ save_secret_string "#{secrets_path}/postgres_password"
64
+ save_secret_string "#{secrets_path}/mail_password"
65
+ save_secret_string "#{secrets_path}/rails_token"
66
+ save_secret_string "#{secrets_path}/devise_token"
67
+ save_secret_string "#{secrets_path}/devise_pepper_token"
68
+ end
69
+
70
+ def save_secret_string(file)
71
+ File.open(file, 'w+') { |f| f.write(SecureRandom.hex(64)) }
72
+ end
73
+
74
+ def create_rsa_certificate(secrets_path, keyout, out)
75
+ "openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 -subj '/C=US/ST=Foo/L=Bar/O=Baz/CN=qux.com' -keyout #{secrets_path}/#{keyout} -out #{secrets_path}/#{out}"
76
+ end
77
+
78
+ def install_role_dependencies
79
+ log_thor_task 'shell', 'Updating ansible roles from the galaxy'
80
+
81
+ galaxy_install =
82
+ "ansible-galaxy install -r #{base_path}/#{Commands::Common::RELATIVE_PATHS[:galaxyfile]} --force"
83
+
84
+ galaxy_out = run(galaxy_install, capture: true)
85
+
86
+ if galaxy_out.include?('you do not have permission')
87
+ if @options[:sudo_password].empty?
88
+ sudo_galaxy_command = 'sudo'
89
+ else
90
+ sudo_galaxy_command = "echo #{@options[:sudo_password]} | sudo -S"
91
+ end
92
+
93
+ run("#{sudo_galaxy_command} #{galaxy_install}")
94
+ end
95
+ end
96
+
97
+ private
98
+
99
+ def fix_path_for_user(file)
100
+ file.sub('templates/includes/', '')
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,54 @@
1
+ require 'orats/commands/common'
2
+ require 'orats/commands/new/ansible'
3
+ require 'orats/commands/new/rails'
4
+ require 'orats/commands/new/foreman'
5
+
6
+ module Orats
7
+ module Commands
8
+ module New
9
+ class Exec < Commands::Common
10
+ include Ansible
11
+ include Rails
12
+ include Foreman
13
+
14
+ def initialize(target_path = '', options = {})
15
+ super
16
+
17
+ @active_path = services_path
18
+ end
19
+
20
+ def init
21
+ rails_template 'base' do
22
+ gsub_postgres_info
23
+ gsub_redis_info unless @options[:redis_password].empty?
24
+ gsub_project_path
25
+
26
+ bundle_install
27
+ bundle_binstubs
28
+ spring_binstub
29
+
30
+ create_and_migrate_database
31
+ end
32
+
33
+ if @options[:auth]
34
+ rails_template 'auth', '--skip ' do
35
+ run_rake 'db:migrate db:seed'
36
+ end
37
+ end
38
+
39
+ ansible_extras unless @options[:skip_extras]
40
+
41
+ custom_rails_template unless @options[:template].empty?
42
+
43
+ foreman_start unless @options[:skip_foreman_start]
44
+ end
45
+
46
+ private
47
+
48
+ def services_path
49
+ @options[:skip_extras] ? @target_path : "#{@target_path}/services/#{File.basename @target_path}"
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,55 @@
1
+ require 'socket'
2
+ require 'timeout'
3
+
4
+ module Orats
5
+ module Commands
6
+ module New
7
+ module Foreman
8
+ def foreman_start
9
+ @options[:skip_foreman_start] ? message = 'Start your' : message = 'Starting'
10
+
11
+ puts '', '='*80
12
+ log_status_top 'action', "#{message} server with the following commands", :cyan
13
+ log_status_bottom 'command', "cd #{@active_path}", :magenta, true
14
+ log_status_bottom 'command', 'bundle exec foreman start', :magenta
15
+ puts '='*80, ''
16
+
17
+ attempt_to_start
18
+ end
19
+
20
+ private
21
+
22
+ def attempt_to_start
23
+ while port_taken? do
24
+ log_status_top 'error', "Another application is using port 3000\n", :red
25
+
26
+ exit 1 if no?('Would you like to try running the server again? (y/N)', :cyan)
27
+ end
28
+
29
+ puts
30
+
31
+ run_from @active_path, 'bundle exec foreman start'
32
+ end
33
+
34
+ def port_taken?
35
+ begin
36
+ Timeout::timeout(5) do
37
+ begin
38
+ s = TCPSocket.new('localhost', 3000)
39
+ s.close
40
+
41
+ return true
42
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
43
+ return false
44
+ end
45
+ end
46
+ rescue Timeout::Error
47
+ false
48
+ end
49
+
50
+ false
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end