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 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