capistrano-atlas 0.0.1

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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/CHANGELOG.md +13 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +215 -0
  7. data/Rakefile +5 -0
  8. data/capistrano-atlas.gemspec +32 -0
  9. data/lib/capistrano/atlas.rb +27 -0
  10. data/lib/capistrano/atlas/compatibility.rb +37 -0
  11. data/lib/capistrano/atlas/dsl.rb +157 -0
  12. data/lib/capistrano/atlas/recipe.rb +49 -0
  13. data/lib/capistrano/atlas/templates/crontab.erb +1 -0
  14. data/lib/capistrano/atlas/templates/csr_config.erb +10 -0
  15. data/lib/capistrano/atlas/templates/logrotate.erb +9 -0
  16. data/lib/capistrano/atlas/templates/maintenance.html.erb +26 -0
  17. data/lib/capistrano/atlas/templates/nginx.erb +64 -0
  18. data/lib/capistrano/atlas/templates/nginx_site.erb +97 -0
  19. data/lib/capistrano/atlas/templates/pgpass.erb +1 -0
  20. data/lib/capistrano/atlas/templates/postgresql-backup-logrotate.erb +11 -0
  21. data/lib/capistrano/atlas/templates/puma.rb.erb +22 -0
  22. data/lib/capistrano/atlas/templates/puma_init.erb +43 -0
  23. data/lib/capistrano/atlas/templates/rbenv_bashrc +4 -0
  24. data/lib/capistrano/atlas/templates/sidekiq_init.erb +100 -0
  25. data/lib/capistrano/atlas/templates/ssl_setup +43 -0
  26. data/lib/capistrano/atlas/templates/version.rb.erb +3 -0
  27. data/lib/capistrano/atlas/version.rb +5 -0
  28. data/lib/capistrano/tasks/aptitude.rake +111 -0
  29. data/lib/capistrano/tasks/bundler.rake +31 -0
  30. data/lib/capistrano/tasks/crontab.rake +14 -0
  31. data/lib/capistrano/tasks/defaults.rake +137 -0
  32. data/lib/capistrano/tasks/dotenv.rake +57 -0
  33. data/lib/capistrano/tasks/logrotate.rake +16 -0
  34. data/lib/capistrano/tasks/maintenance.rake +28 -0
  35. data/lib/capistrano/tasks/migrate.rake +29 -0
  36. data/lib/capistrano/tasks/nginx.rake +25 -0
  37. data/lib/capistrano/tasks/postgresql.rake +149 -0
  38. data/lib/capistrano/tasks/provision.rake +18 -0
  39. data/lib/capistrano/tasks/puma.rake +67 -0
  40. data/lib/capistrano/tasks/rake.rake +20 -0
  41. data/lib/capistrano/tasks/rbenv.rake +104 -0
  42. data/lib/capistrano/tasks/seed.rake +16 -0
  43. data/lib/capistrano/tasks/sidekiq.rake +42 -0
  44. data/lib/capistrano/tasks/ssl.rake +57 -0
  45. data/lib/capistrano/tasks/ufw.rake +32 -0
  46. data/lib/capistrano/tasks/user.rake +32 -0
  47. data/lib/capistrano/tasks/version.rake +34 -0
  48. metadata +161 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0fdb1cb92c6e823effad36625a49db5dccb2ea5b
4
+ data.tar.gz: 3858ea7c0f6e411411fe880d502428686cafc8a5
5
+ SHA512:
6
+ metadata.gz: e6b7c06a79714867e4c37e7fe2aa83756793b2f47467eea82f6d2d9afd25c629e58921882c0b532466a8f89b25afab8e082c6cb6af680819dc2e3691e66b4c9b
7
+ data.tar.gz: f4a9a75485061d6f79045384de70b2e020577b29ac7af225028637627eacbd1fb37ebc753366070ea30d0d26898d65a44363e83a8689da1f114468b7089c053f
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ ## [Unreleased][]
2
+
3
+ * Your contribution here!
4
+
5
+ ## [0.0.1][] (2017-11-01)
6
+
7
+ * Fork, and starting to change things for Puma and Let's Encrypt support in production.
8
+
9
+
10
+
11
+ [Unreleased]: https://github.com/mattbrictson/capistrano-atlas/compare/v0.32.0...HEAD
12
+ [0.32.0]: https://github.com/mattbrictson/capistrano-atlas/compare/v0.31.0...v0.32.0
13
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in capistrano-atlas.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 John McDowall & Matt Brictson
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,215 @@
1
+ # capistrano-atlas
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/capistrano-atlas.svg)](http://badge.fury.io/rb/capistrano-atlas)
4
+
5
+ **This is a fork of `capistrano-mb` by the excellent Matt Brictson and FiftyFive. I've changed things to use Puma and Let's Encrypt in production.**
6
+
7
+ Capistrano is great for deploying Rails applications, but what about all the prerequisites, like Nginx and PostgreSQL? Do you have a firewall configured on your VPS? Have you installed the latest OS security updates? Is HTTPS working right?
8
+
9
+ The capistrano-atlas gem adds a `cap <stage> provision` task to Capistrano that takes care of all that. Out of the box, `provision` will:
10
+
11
+ * Install the latest `postgresql`, `node.js`, and `nginx` apt packages
12
+ * Install all libraries needed to build Ruby
13
+ * Lock down your VPS using `ufw` (a simple front-end to iptables)
14
+ * Set up `logrotated` for your Rails logs
15
+ * Schedule an automatic daily backup of your Rails database
16
+ * Generate a self-signed SSL certificate if you need one
17
+ * Set up ngnix with the latest SSL practices and integrate it with Puma for your Rails app
18
+ * Create the `deployer` user and install an SSH public key
19
+ * Install `rbenv` and use `ruby-build` to compile the version of Ruby required by your app (by inspecting your `.ruby-version` file)
20
+ * And more!
21
+
22
+ The gem is named "capistrano-atlas" because it does the heavy lifting of setting up a VPS to be ready for modern Rails 5.1+ applications.
23
+
24
+ You'll notice that capistrano-atlas is opinionated and strictly uses the following stack:
25
+
26
+ * Ubuntu 14.04 LTS
27
+ * PostgreSQL
28
+ * Puma
29
+ * Nginx
30
+ * rbenv
31
+ * dotenv
32
+
33
+ In addition, capistrano-atlas changes many of Capistrano's defaults, including the deployment location, Bundler behavior, and SSH keep-alive settings. (See [defaults.rake][] for details.)
34
+
35
+ Not quite to your liking? Consider forking the project to meet your needs.
36
+
37
+
38
+ ## Quick start
39
+
40
+ Please note that this project requires **Capistrano 3.x**, which is a complete rewrite of Capistrano 2.x. The two major versions are not compatible.
41
+
42
+ ### 1. Purchase an Ubuntu 14.04 VPS
43
+
44
+ To use capistrano-atlas, you'll need a clean Ubuntu server to deploy to. The only special requirement is that your public SSH key must be installed on the server for the `root` user.
45
+
46
+ Test that you can SSH to the server as `root` without being prompted for a password. If that works, capistrano-atlas can take care of the rest. You're ready to proceed!
47
+
48
+ ### 2. .ruby-version
49
+
50
+ capistrano-atlas needs to know the version of Ruby that your app requires, so that it can install Ruby during the provisioning process. Place a `.ruby-version` file in the root of your project containing the desired version, like this:
51
+
52
+ ```
53
+ 2.2.3
54
+ ```
55
+
56
+ *If you are using `rbenv`, just run `rbenv local 2.2.3` and it will create this file for you.*
57
+
58
+ ### 3. Gemfile
59
+
60
+ capistrano-atlas makes certain assumptions about your Rails app, namely that it uses [dotenv][] to manage Rails secrets via environment variables, and that it runs on top of PostgreSQL and [Puma][]. Make sure they are specified in the Gemfile:
61
+
62
+ ```ruby
63
+ gem "dotenv-rails", ">= 2.0.0"
64
+ gem "pg", "~> 0.18"
65
+ gem "Puma"
66
+ ```
67
+
68
+ Then for the capistrano-atlas tools themselves, add these gems to the development group:
69
+
70
+ ```ruby
71
+ group :development do
72
+ gem "capistrano-bundler", :require => false
73
+ gem "capistrano-rails", :require => false
74
+ gem "capistrano", "~> 3.4.0", :require => false
75
+ gem "capistrano-atlas", :require => false
76
+ end
77
+ ```
78
+
79
+ And then execute:
80
+
81
+ ```
82
+ $ bundle
83
+ ```
84
+
85
+ ### 4. cap install
86
+
87
+ If your project doesn't yet have a `Capfile`, run `cap install` with the list of desired stages (environments). For simplicity, this installation guide will assume a single production stage:
88
+
89
+ ```
90
+ cap install STAGES=production
91
+ ```
92
+
93
+ ### 5. Capfile
94
+
95
+ Add these lines to the **bottom** of your app's `Capfile` (order is important!):
96
+
97
+ ```ruby
98
+ require "capistrano/bundler"
99
+ require "capistrano/rails"
100
+ require "capistrano/atlas"
101
+ ```
102
+
103
+ ### 6. deploy.rb
104
+
105
+ Modify `config/deploy.rb` to set the specifics of your Rails app. At the minimum, you'll need to set these two options:
106
+
107
+ ```ruby
108
+ set :application, "my_app_name"
109
+ set :repo_url, "git@github.com:username/repository.git"
110
+ ```
111
+
112
+ ### 7. production.rb
113
+
114
+ Modify `config/deploy/production.rb` to specify the IP address of your production server. In this example, I have a single 1GB VPS (e.g. at DigitalOcean) that plays all the roles:
115
+
116
+ ```ruby
117
+ server "my.production.ip",
118
+ :user => "deployer",
119
+ :roles => %w(app backup cron db web)
120
+ ```
121
+
122
+ *Note that you must include the `backup` and `cron` roles if you want to make use of capistrano-atlas's database backups and crontab features.*
123
+
124
+ ### 8. secrets.yml
125
+
126
+ By default, Rails 4.2 apps have a `config/secrets.yml` file that specifies the Rails secret key. capistrano-atlas configures dotenv to provide this secret in a `RAILS_SECRET_KEY_BASE` environment variable. You'll therefore need to modify `secrets.yml` as follows:
127
+
128
+ ```ruby
129
+ production:
130
+ secret_key_base: <%= ENV["RAILS_SECRET_KEY_BASE"] %>
131
+ ```
132
+
133
+ ### 9. Provision and deploy!
134
+
135
+ Run capistrano-atlas's `provision:14_04` task (named for Ubuntu 14.04). This will ask you a few questions, install Ruby, PostgreSQL, Nginx, etc., and set everything up. The entire process takes about 10 minutes (mostly due to compiling Ruby from source).
136
+
137
+ ```
138
+ bundle exec cap production provision:14_04
139
+ ```
140
+
141
+ Once that's done, your app is now ready to deploy!
142
+
143
+ ```
144
+ bundle exec cap production deploy
145
+ ```
146
+
147
+ ## Advanced usage
148
+
149
+ ### Choosing which recipes to auto-run
150
+
151
+ Most of the capistrano-atlas recipes are designed to run automatically as part of `cap <stage> provision`, for installing and setting up various bits of the Rails infrastructure, like nginx, Puma, and postgres. Some recipes also contribute to the `cap <stage> deploy` process.
152
+
153
+ *This auto-run behavior is fully under your control.* In your `deploy.rb`, set `:atlas_recipes` to an array of the desired recipes. If you don't want a recipe to execute as part of `deploy`/`provision`, simply omit it from the list.
154
+
155
+ The following list will suffice for most out-of-the-box Rails apps. The order of the list is not important.
156
+
157
+ ```ruby
158
+ set :atlas_recipes, %w(
159
+ aptitude
160
+ crontab
161
+ dotenv
162
+ logrotate
163
+ migrate
164
+ nginx
165
+ postgresql
166
+ rbenv
167
+ seed
168
+ ssl
169
+ ufw
170
+ Puma
171
+ user
172
+ version
173
+ )
174
+ ```
175
+
176
+ Even if you don't include a recipe in the auto-run list, you can still invoke the tasks of those recipes manually at your discretion. Run `bundle exec cap -T` to see the full list of tasks.
177
+
178
+ ### Configuration
179
+
180
+ Many of the recipes have default settings that can be overridden. Use your
181
+ `deploy.rb` file to specify these overrides. Or, you can override per stage.
182
+ Here is an example override:
183
+
184
+ set :atlas_Puma_workers, 8
185
+
186
+ For the full list of settings and their default values, refer to [defaults.rake][].
187
+
188
+
189
+ ## Further reading
190
+
191
+ Check out my [rails-template][] project, which generates Rails applications with capistrano-atlas pre-configured and ready to go.
192
+
193
+
194
+ ## History
195
+
196
+ This gem used to be called capistrano-fiftyfive, because it was initially built by [55 Minutes](http://55minutes.com) to automate its Rails deployments. I have since taken over ownership of the gem and renamed it to capistrano-atlas to avoid any confusion.
197
+
198
+ If you are upgrading from `capistrano-fiftyfive`, refer to the [CHANGELOG entry for v0.22.0](CHANGELOG.md#0220-2015-06-22) for migration instructions.
199
+
200
+ ## Contributing
201
+
202
+ 1. Fork it
203
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
204
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
205
+ 4. Push to the branch (`git push origin my-new-feature`)
206
+ 5. Create new Pull Request
207
+
208
+
209
+ [Postmark]:https://postmarkapp.com
210
+ [cast337]:http://railscasts.com/episodes/337-capistrano-recipes
211
+ [cast373]:http://railscasts.com/episodes/373-zero-downtime-deployment
212
+ [defaults.rake]:lib/capistrano/tasks/defaults.rake
213
+ [rails-template]:https://github.com/mattbrictson/rails-template/
214
+ [dotenv]:https://github.com/bkeepers/dotenv
215
+ [Puma]:http://Puma.bogomips.org/
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+ require "chandler/tasks"
3
+
4
+ # Add chandler as a prerequisite for `rake release`
5
+ #task "release:rubygem_push" => "chandler:push"
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "capistrano/atlas/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "capistrano-atlas"
8
+ spec.version = Capistrano::Atlas::VERSION
9
+ spec.author = "John McDowall"
10
+ spec.email = "john@kantan.io"
11
+ spec.description = \
12
+ "Does all the heavy lifting for production-ready provisioning "\
13
+ "and deployment for the full Rails 5.1 stack. Installs and "\
14
+ "configures Ruby, Nginx, Puma, PostgreSQL, dotenv, Let's Encrypt and "\
15
+ "more onto Ubuntu 14.04 LTS using Capistrano. "
16
+
17
+ spec.summary = "Additional Capistrano 3 recipes"
18
+ spec.homepage = "https://github.com/johnmcdowall/capistrano-atlas"
19
+ spec.license = "MIT"
20
+
21
+ spec.files = `git ls-files`.split($/)
22
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
23
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.add_dependency "capistrano", ">= 3.3.5"
27
+ spec.add_dependency "sshkit", ">= 1.6.1"
28
+
29
+ spec.add_development_dependency "bundler", "~> 1.3"
30
+ spec.add_development_dependency "chandler"
31
+ spec.add_development_dependency "rake"
32
+ end
@@ -0,0 +1,27 @@
1
+ require "digest"
2
+ require "monitor"
3
+ require "capistrano/atlas/version"
4
+ require "capistrano/atlas/compatibility"
5
+ require "capistrano/atlas/dsl"
6
+ require "capistrano/atlas/recipe"
7
+ include Capistrano::Atlas::DSL
8
+
9
+ load File.expand_path("../tasks/provision.rake", __FILE__)
10
+ load File.expand_path("../tasks/defaults.rake", __FILE__)
11
+ load File.expand_path("../tasks/user.rake", __FILE__)
12
+ load File.expand_path("../tasks/aptitude.rake", __FILE__)
13
+ load File.expand_path("../tasks/ufw.rake", __FILE__)
14
+ load File.expand_path("../tasks/ssl.rake", __FILE__)
15
+ load File.expand_path("../tasks/dotenv.rake", __FILE__)
16
+ load File.expand_path("../tasks/postgresql.rake", __FILE__)
17
+ load File.expand_path("../tasks/nginx.rake", __FILE__)
18
+ load File.expand_path("../tasks/crontab.rake", __FILE__)
19
+ load File.expand_path("../tasks/logrotate.rake", __FILE__)
20
+ load File.expand_path("../tasks/rbenv.rake", __FILE__)
21
+ load File.expand_path("../tasks/maintenance.rake", __FILE__)
22
+ load File.expand_path("../tasks/migrate.rake", __FILE__)
23
+ load File.expand_path("../tasks/seed.rake", __FILE__)
24
+ load File.expand_path("../tasks/version.rake", __FILE__)
25
+ load File.expand_path("../tasks/rake.rake", __FILE__)
26
+ load File.expand_path("../tasks/sidekiq.rake", __FILE__)
27
+ load File.expand_path("../tasks/bundler.rake", __FILE__)
@@ -0,0 +1,37 @@
1
+ module Capistrano
2
+ module Atlas
3
+ module Compatibility
4
+ def self.check
5
+ check_capistrano_and_rake_are_loaded
6
+ check_blacklisted_capistrano_version
7
+ end
8
+
9
+ def self.check_capistrano_and_rake_are_loaded
10
+ return if defined?(Capistrano::VERSION) && defined?(Rake)
11
+
12
+ warn "capistrano/atlas must be loaded by Capistrano in order "\
13
+ "to work.\nRequire this gem by using Capistrano's Capfile, "\
14
+ "as described here:\n"\
15
+ "https://github.com/mattbrictson/capistrano-atlas#installation"
16
+ end
17
+
18
+ def self.check_blacklisted_capistrano_version
19
+ return unless defined?(Capistrano::VERSION)
20
+ return unless Capistrano::VERSION == "3.2.0"
21
+
22
+ warn "Capistrano 3.2.0 has a critical bug that prevents "\
23
+ "capistrano-atlas from working as intended:\n"\
24
+ "https://github.com/capistrano/capistrano/issues/1004"
25
+ end
26
+
27
+ # We can't really rely on anything being loaded at this point, so define
28
+ # our own basic colorizing helper.
29
+ def self.warn(message)
30
+ return $stderr.puts("WARNING: #{message}") unless $stderr.tty?
31
+ $stderr.puts("\e[0;31;49mWARNING: #{message}\e[0m")
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ Capistrano::Atlas::Compatibility.check
@@ -0,0 +1,157 @@
1
+ module Capistrano
2
+ module Atlas
3
+ module DSL
4
+
5
+ # Invoke the given task. If a task with that name is not defined,
6
+ # silently skip it.
7
+ #
8
+ def invoke_if_defined(task)
9
+ invoke(task) if Rake::Task.task_defined?(task)
10
+ end
11
+
12
+ # Used internally by capistrano-atlas to register tasks such that
13
+ # those tasks are executed conditionally based on the presence of the
14
+ # recipe name in fetch(:atlas_recipes).
15
+ #
16
+ # atlas_recipe :aptitude do
17
+ # during :provision, %w(task1 task2 ...)
18
+ # end
19
+ #
20
+ def atlas_recipe(recipe_name, &block)
21
+ Recipe.new(recipe_name).instance_exec(&block)
22
+ end
23
+
24
+ def compatibility_warning(warning)
25
+ Capistrano::Atlas::Compatibility.warn(warning)
26
+ end
27
+
28
+ # Helper for calling fetch(:application) and making the value safe for
29
+ # using in filenames, usernames, etc. Replaces non-word characters with
30
+ # underscores.
31
+ #
32
+ def application_basename
33
+ fetch(:application).to_s.gsub(/[^a-zA-Z0-9_]/, "_")
34
+ end
35
+
36
+ # Prints a question and returns truthy if the user answers "y" or "yes".
37
+ def agree(yes_or_no_question)
38
+ $stdout.print(yes_or_no_question)
39
+ $stdin.gets.to_s =~ /^y(es)?/i
40
+ end
41
+
42
+ # Like capistrano's built-in on(), but connects to the server as root.
43
+ # To use a user other than root, set :atlas_privileged_user or
44
+ # specify :privileged_user as a server property.
45
+ #
46
+ # task :reboot do
47
+ # privileged_on roles(:all) do
48
+ # execute :shutdown, "-r", "now"
49
+ # end
50
+ # end
51
+ #
52
+ def privileged_on(*args, &block)
53
+ on(*args) do |host|
54
+ if host.nil?
55
+ instance_exec(nil, nil, &block)
56
+ else
57
+ original_user = host.user
58
+
59
+ begin
60
+ host.user = host.properties.privileged_user ||
61
+ fetch(:atlas_privileged_user)
62
+ instance_exec(host, original_user, &block)
63
+ ensure
64
+ host.user = original_user
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ # Uploads the given string or file-like object to the current host
71
+ # context. Intended to be used within an on() or privileged_on() block.
72
+ # Accepts :owner and :mode options that affect the permissions of the
73
+ # remote file.
74
+ #
75
+ def put(string_or_io, remote_path, opts={})
76
+ sudo_exec = ->(*cmd) {
77
+ cmd = [:sudo] + cmd if opts[:sudo]
78
+ execute *cmd
79
+ }
80
+
81
+ tmp_path = "/tmp/#{SecureRandom.uuid}"
82
+
83
+ owner = opts[:owner]
84
+ mode = opts[:mode]
85
+
86
+ source = if string_or_io.respond_to?(:read)
87
+ string_or_io
88
+ else
89
+ StringIO.new(string_or_io.to_s)
90
+ end
91
+
92
+ sudo_exec.call :mkdir, "-p", File.dirname(remote_path)
93
+
94
+ upload!(source, tmp_path)
95
+
96
+ sudo_exec.call(:mv, "-f", tmp_path, remote_path)
97
+ sudo_exec.call(:chown, owner, remote_path) if owner
98
+ sudo_exec.call(:chmod, mode, remote_path) if mode
99
+ end
100
+
101
+
102
+ # Read the specified file from the local system, interpret it as ERb,
103
+ # and upload it to the current host context. Intended to be used with an
104
+ # on() or privileged_on() block. Accepts :owner, :mode, and :binding
105
+ # options.
106
+ #
107
+ # Templates with relative paths are first searched for in
108
+ # lib/capistrano/atlas/templates in the current project. This gives
109
+ # applications a chance to override. If an override is not found, the
110
+ # default template within the capistrano-atlas gem is used.
111
+ #
112
+ # task :create_database_yml do
113
+ # on roles(:app, :db) do
114
+ # within(shared_path) do
115
+ # template fetch(:database_yml_template_path),
116
+ # "config/database.yml",
117
+ # :mode => "600"
118
+ # end
119
+ # end
120
+ # end
121
+ #
122
+ def template(local_path, remote_path, opts={})
123
+ binding = opts[:binding] || binding
124
+
125
+ unless local_path.start_with?("/")
126
+ override_path = File.join("lib/capistrano/atlas/templates", local_path)
127
+
128
+ unless File.exist?(override_path)
129
+ override_path = File.join(
130
+ "lib/capistrano/atlas/templates",
131
+ local_path
132
+ )
133
+ if File.exist?(override_path)
134
+ compatibility_warning(
135
+ "Please move #{override_path} from lib/capistrano/atlas "\
136
+ "to lib/capistrano/atlas to ensure future compatibility with "\
137
+ "capistrano-atlas."
138
+ )
139
+ end
140
+ end
141
+
142
+ local_path = if File.exist?(override_path)
143
+ override_path
144
+ else
145
+ File.expand_path(File.join("../templates", local_path), __FILE__)
146
+ end
147
+ end
148
+
149
+ erb = File.read(local_path)
150
+ rendered_template = ERB.new(erb).result(binding)
151
+
152
+ put(rendered_template, remote_path, opts)
153
+ end
154
+ end
155
+ end
156
+ end
157
+