k3_capistrano 1.0.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.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/Readme.md +86 -0
- data/k3_capistrano.gemspec +30 -0
- data/lib/capistrano_database_yml.rb +142 -0
- data/lib/k3/capistrano.rb +2 -0
- data/lib/k3_capistrano.rb +5 -0
- data/lib/k3_capistrano/asset_pipeline.rb +316 -0
- data/lib/k3_capistrano/capistrano.rb +132 -0
- data/lib/k3_capistrano/chef.rb +41 -0
- data/lib/k3_capistrano/db.rb +72 -0
- data/lib/k3_capistrano/db_backups.rb +207 -0
- data/lib/k3_capistrano/ensure_db_is_set_up.rb +34 -0
- data/lib/k3_capistrano/git.rb +37 -0
- data/lib/k3_capistrano/logs.rb +20 -0
- data/lib/k3_capistrano/ssh.rb +14 -0
- data/lib/k3_capistrano/test_request.rb +16 -0
- data/lib/k3_capistrano/test_results.rb +85 -0
- data/lib/k3_capistrano/unicorn.rb +30 -0
- data/lib/k3_capistrano/version.rb +5 -0
- data/lib/templates/database.yml.erb +8 -0
- data/lib/test_result_logging.rb +79 -0
- metadata +164 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/Readme.md
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
k3_capistrano
|
2
|
+
=============
|
3
|
+
|
4
|
+
The k3_capistrano gem sets some reasonable defaults so that your app's config/deploy* files can be
|
5
|
+
very minimal and ideally only need to include any app-specific configuration.
|
6
|
+
|
7
|
+
It also adds some additional deploy tasks such as deploy:test_request (see below for a list).
|
8
|
+
|
9
|
+
Getting started
|
10
|
+
===============
|
11
|
+
|
12
|
+
Add k3_capistrano to your Gemfile and add something like this to your config/deploy.rb:
|
13
|
+
|
14
|
+
set :application, 'app_name'
|
15
|
+
set :repository, "user@server:#{application}.git"
|
16
|
+
|
17
|
+
require 'k3_capistrano/capistrano'
|
18
|
+
|
19
|
+
Add your staging/production -server-specific config in config/deploy/staging.rb /
|
20
|
+
config/deploy/production.rb, respectively. For example:
|
21
|
+
|
22
|
+
set :domain, 'my.domain.org'
|
23
|
+
|
24
|
+
To deploy, you just do the usual:
|
25
|
+
|
26
|
+
cap deploy # defaults to staging
|
27
|
+
|
28
|
+
or to deploy to production:
|
29
|
+
|
30
|
+
cap production deploy
|
31
|
+
|
32
|
+
If your app needs to, it can override any of the defaults, for example these:
|
33
|
+
|
34
|
+
set :deploy_to, "/apps/#{application}"
|
35
|
+
set :branch, 'master' # The git branch to deploy. Could also be set in config/deploy/#{stage}.rb
|
36
|
+
|
37
|
+
Integrating with Chef
|
38
|
+
=====================
|
39
|
+
|
40
|
+
If you are using Chef and would like capistrano to simply use the configuration that you've already
|
41
|
+
defined for this app using Chef, you can follow this alternate instructions to avoid duplicating
|
42
|
+
that configuration data in two places...
|
43
|
+
|
44
|
+
Set the chef_home environment variable to point to your copy of K3's deployment/kitchen/.
|
45
|
+
|
46
|
+
Just add a line like this to your .bashrc and source your .bashrc:
|
47
|
+
|
48
|
+
export chef_home=/path/to/deployment/kitchen/
|
49
|
+
|
50
|
+
Add k3_capistrano to your Gemfile and add something like this to your config/deploy.rb:
|
51
|
+
|
52
|
+
set :application, 'app_name'
|
53
|
+
require 'k3_capistrano/chef'
|
54
|
+
require 'k3_capistrano/capistrano'
|
55
|
+
|
56
|
+
|
57
|
+
Tasks provided by k3_capistrano
|
58
|
+
===============================
|
59
|
+
|
60
|
+
cap backup:create # Back up the database
|
61
|
+
cap backup:download # Download the latest backup
|
62
|
+
cap backup:locate_last # Locate the latest backup
|
63
|
+
cap backup:restore # Restore a database backup file.
|
64
|
+
cap backup:upload_backup # Upload a database backup file to the ...
|
65
|
+
cap db:database_yml:setup # Creates the database.yml file under t...
|
66
|
+
cap db:pull # Pull data from remote database to you...
|
67
|
+
cap db:pull_to_staging # Pull data from production database to...
|
68
|
+
cap db:setup # Run the db:setup rake task.
|
69
|
+
cap db:taps_server # This uses your database config from y...
|
70
|
+
cap deploy:test_request # Does a test request to the web site s...
|
71
|
+
cap git:check_if_needs_push # Checks to see if your branch is ahead...
|
72
|
+
cap git:push # Pushes your local commits to the git ...
|
73
|
+
cap logs:tail # tail all 4 app log files to help you ...
|
74
|
+
cap logs:watch # tail -f the production.log file
|
75
|
+
|
76
|
+
Skipping steps
|
77
|
+
==============
|
78
|
+
|
79
|
+
This deploy script (and its before/after hooks) will take care of doing everything, including
|
80
|
+
running db:migrate and precompiling your assets.
|
81
|
+
|
82
|
+
Since some of those steps aren't necessary for every signle deploy and can make your deploy take
|
83
|
+
longer than it needs to, you can skip some of those steps by setting the skip environment variable
|
84
|
+
when you run cap deploy. For example, to skip the migrate and assets steps, just run this:
|
85
|
+
|
86
|
+
skip=assets,migrate cap production deploy
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "k3_capistrano/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "k3_capistrano"
|
7
|
+
s.version = K3Capistrano.version
|
8
|
+
s.authors = ["Tyler Rick"]
|
9
|
+
s.email = ["tyler@k3integrations.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Common Capistrano tasks and defaults used by K3 Integrations}
|
12
|
+
s.description = s.summary
|
13
|
+
|
14
|
+
s.rubyforge_project = "k3_capistrano"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_runtime_dependency 'parseconfig'
|
22
|
+
s.add_runtime_dependency 'capistrano-ext'
|
23
|
+
s.add_runtime_dependency 'capistrano_colors'
|
24
|
+
s.add_runtime_dependency 'facets'
|
25
|
+
s.add_runtime_dependency 'rvm-capistrano'
|
26
|
+
s.add_runtime_dependency 'colored'
|
27
|
+
|
28
|
+
#s.add_runtime_dependency 'tylerrick-chef'
|
29
|
+
#s.add_runtime_dependency 'capistrano-chef'
|
30
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
#
|
2
|
+
# = Capistrano database.yml task
|
3
|
+
#
|
4
|
+
# Provides a couple of tasks for creating the database.yml
|
5
|
+
# configuration file dynamically when deploy:setup is run.
|
6
|
+
#
|
7
|
+
# Category:: Capistrano
|
8
|
+
# Package:: Database
|
9
|
+
# Author:: Simone Carletti <weppos@weppos.net>
|
10
|
+
# Copyright:: 2007-2010 The Authors
|
11
|
+
# License:: MIT License
|
12
|
+
# Link:: http://www.simonecarletti.com/
|
13
|
+
# Source:: http://gist.github.com/2769
|
14
|
+
#
|
15
|
+
#
|
16
|
+
# == Requirements
|
17
|
+
#
|
18
|
+
# This extension requires the original <tt>config/database.yml</tt> to be excluded
|
19
|
+
# from source code checkout. You can easily accomplish this by renaming
|
20
|
+
# the file (for example to database.example.yml) and appending <tt>database.yml</tt>
|
21
|
+
# value to your SCM ignore list.
|
22
|
+
#
|
23
|
+
# # Example for Subversion
|
24
|
+
#
|
25
|
+
# $ svn mv config/database.yml config/database.example.yml
|
26
|
+
# $ svn propset svn:ignore 'database.yml' config
|
27
|
+
#
|
28
|
+
# # Example for Git
|
29
|
+
#
|
30
|
+
# $ git mv config/database.yml config/database.example.yml
|
31
|
+
# $ echo 'config/database.yml' >> .gitignore
|
32
|
+
#
|
33
|
+
#
|
34
|
+
# == Usage
|
35
|
+
#
|
36
|
+
# Include this file in your <tt>deploy.rb</tt> configuration file.
|
37
|
+
# Assuming you saved this recipe as capistrano_database_yml.rb:
|
38
|
+
#
|
39
|
+
# require "capistrano_database_yml"
|
40
|
+
#
|
41
|
+
# Now, when <tt>deploy:setup</tt> is called, this script will automatically
|
42
|
+
# create the <tt>database.yml</tt> file in the shared folder.
|
43
|
+
# Each time you run a deploy, this script will also create a symlink
|
44
|
+
# from your application <tt>config/database.yml</tt> pointing to the shared configuration file.
|
45
|
+
#
|
46
|
+
# == Custom template
|
47
|
+
#
|
48
|
+
# By default, this script creates an exact copy of the default
|
49
|
+
# <tt>database.yml</tt> file shipped with a new Rails 2.x application.
|
50
|
+
# If you want to overwrite the default template, simply create a custom Erb template
|
51
|
+
# called <tt>database.yml.erb</tt> and save it into <tt>config/deploy</tt> folder.
|
52
|
+
#
|
53
|
+
# Although the name of the file can't be changed, you can customize the directory
|
54
|
+
# where it is stored defining a variable called <tt>:template_dir</tt>.
|
55
|
+
#
|
56
|
+
# # store your custom template at foo/bar/database.yml.erb
|
57
|
+
# set :template_dir, "foo/bar"
|
58
|
+
#
|
59
|
+
# # example of database template
|
60
|
+
#
|
61
|
+
# base: &base
|
62
|
+
# adapter: sqlite3
|
63
|
+
# timeout: 5000
|
64
|
+
# development:
|
65
|
+
# database: #{shared_path}/db/development.sqlite3
|
66
|
+
# <<: *base
|
67
|
+
# test:
|
68
|
+
# database: #{shared_path}/db/test.sqlite3
|
69
|
+
# <<: *base
|
70
|
+
# production:
|
71
|
+
# adapter: mysql
|
72
|
+
# database: #{application}_production
|
73
|
+
# username: #{user}
|
74
|
+
# password: #{Capistrano::CLI.ui.ask("Enter MySQL database password: ")}
|
75
|
+
# encoding: utf8
|
76
|
+
# timeout: 5000
|
77
|
+
#
|
78
|
+
# Because this is an Erb template, you can place variables and Ruby scripts
|
79
|
+
# within the file.
|
80
|
+
# For instance, the template above takes advantage of Capistrano CLI
|
81
|
+
# to ask for a MySQL database password instead of hard coding it into the template.
|
82
|
+
#
|
83
|
+
# === Password prompt
|
84
|
+
#
|
85
|
+
# For security reasons, in the example below the password is not
|
86
|
+
# hard coded (or stored in a variable) but asked on setup.
|
87
|
+
# I don't like to store passwords in files under version control
|
88
|
+
# because they will live forever in your history.
|
89
|
+
# This is why I use the Capistrano::CLI utility.
|
90
|
+
#
|
91
|
+
|
92
|
+
unless Capistrano::Configuration.respond_to?(:instance)
|
93
|
+
abort "This extension requires Capistrano 2"
|
94
|
+
end
|
95
|
+
|
96
|
+
require 'pathname'
|
97
|
+
|
98
|
+
Capistrano::Configuration.instance.load do
|
99
|
+
|
100
|
+
namespace :db do
|
101
|
+
namespace :database_yml do
|
102
|
+
|
103
|
+
desc <<-DESC
|
104
|
+
Creates the database.yml file under the shared dir on the server.
|
105
|
+
|
106
|
+
During a deploy, the db:database_yml:symlink task will symlink from config/database.yml to
|
107
|
+
the shared file.
|
108
|
+
|
109
|
+
By default, this task uses the default template found in lib/templates/database.yml.erb of
|
110
|
+
the k3_capistrano gem unless a template called database.yml.erb is found either in your
|
111
|
+
app's config/deploy directory.
|
112
|
+
DESC
|
113
|
+
task :setup, :except => { :no_release => true } do
|
114
|
+
upload_to = "#{shared_path}/config/database.yml"
|
115
|
+
|
116
|
+
default_template = Pathname.new(__FILE__).dirname + 'templates/database.yml.erb'
|
117
|
+
|
118
|
+
override_template = Pathname.new(fetch(:template_dir, "config/deploy")) + 'database.yml.erb'
|
119
|
+
template = override_template.file? ? override_template.read : default_template.read
|
120
|
+
|
121
|
+
config = ERB.new(template).result(binding)
|
122
|
+
puts "Installing this database.yml to #{upload_to}:\n#{config}"
|
123
|
+
|
124
|
+
run "mkdir -p #{shared_path}/db"
|
125
|
+
run "mkdir -p #{shared_path}/config"
|
126
|
+
put config, upload_to
|
127
|
+
end
|
128
|
+
|
129
|
+
desc <<-DESC
|
130
|
+
[internal] Updates the symlink for database.yml file to the just deployed release.
|
131
|
+
DESC
|
132
|
+
task :symlink, :except => { :no_release => true } do
|
133
|
+
run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml"
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
before 'db:database_yml:setup', 'db:read_remote_my_cnf' # lib/templates/database.yml.erb gets password from my_cnf
|
140
|
+
after "deploy:setup", "db:database_yml:setup" unless fetch(:skip_db_setup, false)
|
141
|
+
after "deploy:finalize_update", "db:database_yml:symlink"
|
142
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
# This file will be automatically required if the k3_capistrano gem is in your
|
2
|
+
# Gemfile, so we don't want to put anything Capistrano-dependent here in case
|
3
|
+
# they aren't loading/running cap.
|
4
|
+
#
|
5
|
+
# To actually load this into your config/deploy.rb, you need to explicitly require 'k3_capistrano/capistrano' instead.
|
@@ -0,0 +1,316 @@
|
|
1
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
2
|
+
|
3
|
+
# From Rails 3.1 and later, we should assume they want to use the asset pipeline. Can be disabled by setting to false.
|
4
|
+
_cset :asset_pipeline, true
|
5
|
+
|
6
|
+
if asset_pipeline
|
7
|
+
#===================================================================================================
|
8
|
+
# Standard recipe
|
9
|
+
|
10
|
+
load 'deploy/assets'
|
11
|
+
# The assets recipes that are included with capistrano work like this:
|
12
|
+
# 1. deploy:assets:symlink creates a symlink from #{latest_release}/public/assets to #{shared_path}/assets
|
13
|
+
# 2. Runs rake assets:precompile on the server
|
14
|
+
#
|
15
|
+
# We will make use of #1, but override #2 as needed to also allow assets to precompiled locally on
|
16
|
+
# the developer's localhost.
|
17
|
+
|
18
|
+
#===================================================================================================
|
19
|
+
# More libraries and requires
|
20
|
+
|
21
|
+
require 'pp'
|
22
|
+
require 'facets/class/to_proc'
|
23
|
+
require 'facets/string/cleanlines'
|
24
|
+
require 'colored'
|
25
|
+
|
26
|
+
# We can just get the digests from the manifest.yml instead of recomputing them!
|
27
|
+
# Also, when I had it recomputing the digest of every file, it would give false positives for a
|
28
|
+
# bunch of .gz files. Apparently, gzipping the same file twice can produce a file with a different
|
29
|
+
# digest each time? Anyway, if the source file that the .gz file was produced to hasn't changed, we
|
30
|
+
# don't care if the .gz file itself is not identical.
|
31
|
+
#Pathname.class_eval do
|
32
|
+
# def digest
|
33
|
+
# Digest::MD5.file(@path).hexdigest
|
34
|
+
# end
|
35
|
+
#end
|
36
|
+
class K3Capistrano::AssetManifest
|
37
|
+
attr_reader :digests
|
38
|
+
def initialize(manifest_file)
|
39
|
+
@manifest_file = manifest_file
|
40
|
+
@digests = YAML.load_file(manifest_file)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Compare to digest_for actionpack-3.2.11/lib/sprockets/helpers/rails_helper.rb
|
44
|
+
# We could just reuse that, if we were okay with it loading the Rails application (in order to get
|
45
|
+
# Rails.application.config.assets)
|
46
|
+
def digest_for!(logical_path)
|
47
|
+
digest_for(logical_path) or (raise "#{logical_path} not found in #{@manifest_file}" if logical_path =~ /\.js$|\.css$/)
|
48
|
+
end
|
49
|
+
|
50
|
+
def digest_for(logical_path)
|
51
|
+
#puts %(@digests[#{logical_path}]=#{(@digests[logical_path]).inspect})
|
52
|
+
@digests[logical_path]
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_s
|
56
|
+
@manifest_file.to_s
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
#===================================================================================================
|
61
|
+
# Variables
|
62
|
+
|
63
|
+
_cset :precompile_assets_locally, false
|
64
|
+
# The following options only apply if precompile_assets_locally is true:
|
65
|
+
(
|
66
|
+
# Set skip to include 'local_precompile' to skip running assets:precompile locally during this deploy.
|
67
|
+
# You may want to do this to speed up the deploy if you know that your local assets directory is
|
68
|
+
# already current -- for example, if you just deployed to staging (and did the precompile during
|
69
|
+
# that deploy) and now you are ready to deploy the exact same code to production.
|
70
|
+
|
71
|
+
# Can also set to :previous_local
|
72
|
+
#_cset :compare_to_manifest_from, :remote
|
73
|
+
|
74
|
+
# If true, keeps a local public/assets; if false, renames it immediately after running precompile
|
75
|
+
# It is recommended to set this to false, because if a public/assets dir exists in development mode,
|
76
|
+
# changes to your assets files will *not* take effect until you run precompile, whereas if there is
|
77
|
+
# *no* public/assets dir, changes will be compiled automatically as needed and take immediately
|
78
|
+
# after changing them.
|
79
|
+
set :keep_public_assets_dir, false
|
80
|
+
|
81
|
+
# TODO: How many backups of public/assets to keep
|
82
|
+
set :local_asset_dirs_to_keep, 5
|
83
|
+
|
84
|
+
# Set to false if you don't want it to show the list of changed assets during a deploy.
|
85
|
+
set :list_changed_assets_during_deploy, true
|
86
|
+
)
|
87
|
+
|
88
|
+
#===================================================================================================
|
89
|
+
namespace :deploy do
|
90
|
+
namespace :assets do
|
91
|
+
|
92
|
+
# If you already know that no changes have been made to your assets since your last deploy, you
|
93
|
+
# can speed up the deploy by several minutes by skipping the precompilation step and just reusing
|
94
|
+
# the assets in shared/assets that were used by the previous deploy.
|
95
|
+
#
|
96
|
+
# Usage: skip=assets cap deploy
|
97
|
+
#
|
98
|
+
if skip.include?('assets')
|
99
|
+
task :precompile, :roles => :app, :except => { :no_release => false } do
|
100
|
+
# Do nothing.
|
101
|
+
puts "Warning: Skipping the 'deploy:assets:precompile' task. Make sure the assets in shared/assets are current."
|
102
|
+
end
|
103
|
+
|
104
|
+
else
|
105
|
+
if precompile_assets_locally
|
106
|
+
# This strategy precompiles your assets locally and then copies the files to the remote server.
|
107
|
+
# This way you can deploy to multiple servers in a row without having to recompile the assets each time,
|
108
|
+
# and you can redeploy to the same server without recompiling as long as no assets have changed.
|
109
|
+
# You must set :precompile_assets_locally, true if you want to opt in to using this.
|
110
|
+
#
|
111
|
+
# Overview:
|
112
|
+
# * Downloads current manifest from shared/assets/manifest.yml on server.
|
113
|
+
# * For each file in local public/assets dir (or most recent timestamped dir):
|
114
|
+
# * If local file not present in currently deployed manifest, upload it
|
115
|
+
# * If local file has different digest, upload it
|
116
|
+
# Do incremental update instead of rsyncing all
|
117
|
+
|
118
|
+
# List all asset dirs (dirs matching public/assets.*), sorted by filename, except always returning
|
119
|
+
# public/assets last if it exists.
|
120
|
+
def local_asset_dirs
|
121
|
+
(
|
122
|
+
Dir["public/assets.*"].map(&Pathname).sort +
|
123
|
+
[Pathname.new('public/assets')]
|
124
|
+
).select(&:directory?).select(&:exist?).to_a
|
125
|
+
end
|
126
|
+
|
127
|
+
task :list_local_asset_dirs do
|
128
|
+
puts local_asset_dirs
|
129
|
+
end
|
130
|
+
|
131
|
+
def changed_from(previous_file_list, previous_manifest, verbose = false)
|
132
|
+
current_dir = local_asset_dirs.last or raise "no local public/assets* dirs exist"
|
133
|
+
current_manifest = K3Capistrano::AssetManifest.new(current_dir + 'manifest.yml')
|
134
|
+
|
135
|
+
if verbose
|
136
|
+
puts "Listing changes between '#{previous_manifest}' (previous) and '#{current_manifest}' (latest local precompile, at '#{current_dir}')..."
|
137
|
+
puts "Legend: " + "doesn't exist (in dir of '#{previous_manifest}')".cyan + ' | ' + "exists but has changed since '#{previous_manifest}'".yellow
|
138
|
+
end
|
139
|
+
|
140
|
+
descendants = []; current_dir.find {|_| descendants << _ if _.file? }
|
141
|
+
descendants.select do |file_in_current|
|
142
|
+
base_name = file_in_current.relative_path_from(current_dir).to_s.gsub(%r<^../>, '')
|
143
|
+
logical_path = base_name.gsub(/-\w{32}/, '').gsub(/\.gz$/, '')
|
144
|
+
#file_in_previous = previous_dir + base_name if previous_dir
|
145
|
+
|
146
|
+
# Only if the file *exists* in and is in the manifest can we skip it.
|
147
|
+
# This is a safeguard against the case where the manifest.yml file gets uploaded but the
|
148
|
+
# files themselves haven't yet.
|
149
|
+
|
150
|
+
file_in_previous = previous_file_list.include?(base_name) && previous_manifest.digest_for(logical_path)
|
151
|
+
changed =
|
152
|
+
if file_in_previous #&& file_in_previous.exist?
|
153
|
+
if previous_manifest.digest_for!(logical_path) == current_manifest.digest_for!(logical_path)
|
154
|
+
color = :green
|
155
|
+
false
|
156
|
+
else
|
157
|
+
color = :yellow
|
158
|
+
true
|
159
|
+
end
|
160
|
+
else
|
161
|
+
color = :cyan
|
162
|
+
true
|
163
|
+
end
|
164
|
+
puts file_in_current.to_s.send(color) if verbose && changed
|
165
|
+
changed
|
166
|
+
end.map do |_|
|
167
|
+
_.relative_path_from(current_dir).to_s.gsub(%r<^../>, '')
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def changed_since_previous_local_precompile(verbose = false)
|
172
|
+
# TODO: It is also possible for there to be no previous assets dir, in which case it should
|
173
|
+
# list *all* files in latest_dir as being new files.
|
174
|
+
previous_dir = local_asset_dirs[-2]
|
175
|
+
previous_file_list = []; previous_dir.find {|_| previous_file_list << _ if _.file? }
|
176
|
+
latest_dir = local_asset_dirs.last
|
177
|
+
puts "Listing changes between '#{previous_dir}' and latest local precompile, '#{latest_dir}'..." if verbose
|
178
|
+
previous_manifest = K3Capistrano::AssetManifest.new(previous_dir + 'manifest.yml')
|
179
|
+
changed_from(previous_file_list, previous_manifest, verbose)
|
180
|
+
end
|
181
|
+
|
182
|
+
def remote_file_list
|
183
|
+
capture("cd #{shared_path}/assets && find . -type f").
|
184
|
+
cleanlines.map {|_| _.gsub(%r<^\./>, '') }
|
185
|
+
end
|
186
|
+
|
187
|
+
def changed_since_last_deploy(verbose = false)
|
188
|
+
remote_manifest = 'tmp/manifest_from_last_deploy.yml'
|
189
|
+
get "#{shared_path}/assets/manifest.yml", remote_manifest, via: :scp
|
190
|
+
remote_manifest = K3Capistrano::AssetManifest.new(remote_manifest)
|
191
|
+
remote_file_list = remote_file_list()
|
192
|
+
Pathname('tmp/asset_file_from_last_deploy.txt').tap do |file|
|
193
|
+
puts %(Saving list of files from server at: #{file})
|
194
|
+
file.open('w') {|_| _.puts remote_file_list.join("\n" )}
|
195
|
+
end
|
196
|
+
puts "Listing changes between latest deploy and latest local precompile..." if verbose
|
197
|
+
changed_from(remote_file_list, remote_manifest, verbose)
|
198
|
+
end
|
199
|
+
|
200
|
+
def changed_assets(verbose = false)
|
201
|
+
#case compare_to_manifest_from
|
202
|
+
#when :remote
|
203
|
+
changed_since_last_deploy(verbose)
|
204
|
+
#when :previous_local
|
205
|
+
# changed_since_previous_local_precompile(verbose)
|
206
|
+
#else
|
207
|
+
# raise "unrecognized value for compare_to_manifest_from: #{compare_to_manifest_from.inspect}"
|
208
|
+
#end
|
209
|
+
end
|
210
|
+
|
211
|
+
task :list_changed_since_previous_local_precompile do
|
212
|
+
puts changed_since_previous_local_precompile(verbose = true)
|
213
|
+
end
|
214
|
+
|
215
|
+
task :list_changed_assets do
|
216
|
+
changed_assets(verbose = true)
|
217
|
+
end
|
218
|
+
|
219
|
+
task :list_changed_since_last_deploy do
|
220
|
+
changed_since_last_deploy(verbose = true)
|
221
|
+
end
|
222
|
+
|
223
|
+
task :push_changed_assets do
|
224
|
+
current_dir = local_asset_dirs.last or raise "no local public/assets* dirs exist"
|
225
|
+
changed_files = changed_assets(verbose = list_changed_assets_during_deploy)
|
226
|
+
|
227
|
+
#remote_host_and_path = "#{user}@#{domain}:#{release_or_current_path.gsub('~/', './')}/public/assets/"
|
228
|
+
remote_host_and_path = "#{user}@#{domain}:#{shared_path}/assets/"
|
229
|
+
|
230
|
+
changed_files_in_root, changed_files_in_subdir = changed_files.partition {|_|
|
231
|
+
Pathname.new(_).dirname.to_s == '.'
|
232
|
+
}
|
233
|
+
if changed_files_in_root.any?
|
234
|
+
# TODO: shell escape arguments
|
235
|
+
command = "time scp " +
|
236
|
+
"#{changed_files_in_root.map {|_| "#{current_dir}/#{_}" }.join(' ')} " +
|
237
|
+
"#{remote_host_and_path}"
|
238
|
+
#command = "rsync -a --itemize-changes --recursive --compress --times #{changed_files} #{remote_host_and_path}"
|
239
|
+
run_locally command
|
240
|
+
|
241
|
+
# See also this version from https://groups.google.com/forum/#!topic/capistrano/cuOeI-aNLfo
|
242
|
+
# servers = find_servers_for_task(current_task)
|
243
|
+
# port_option = port ? " -e 'ssh -p #{port}' " : ''
|
244
|
+
# servers.each do |server|
|
245
|
+
# run_locally("rsync --recursive --times --rsh=ssh --compress --human-readable #{port_option} --progress public/assets #{user} @#{server}:#{shared_path}")
|
246
|
+
# end
|
247
|
+
end
|
248
|
+
|
249
|
+
remote_dir_list = remote_file_list().map {|_| Pathname(_).dirname }.uniq
|
250
|
+
changed_files_in_subdir.each do |_|
|
251
|
+
# mkdir subdir if it doesn't exist on server. (Check in remote_file_list so we don't
|
252
|
+
# have to hit the server to check each file individually.)
|
253
|
+
dir = Pathname(_).dirname
|
254
|
+
unless remote_dir_list.include? dir
|
255
|
+
remote_dir_list << dir
|
256
|
+
run "mkdir -p #{shared_path}/assets/#{dir}"
|
257
|
+
end
|
258
|
+
|
259
|
+
#put "#{current_dir}/#{_}", "#{shared_path}/assets/#{_}", via: :scp
|
260
|
+
command = "scp #{"#{current_dir}/#{_}"} #{remote_host_and_path}#{_}"
|
261
|
+
run_locally command
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def skip_local_precompile?
|
266
|
+
skip.include?('local_precompile')
|
267
|
+
end
|
268
|
+
|
269
|
+
def local_asset_current?
|
270
|
+
# TODO: Compare time of last commit to app/assets or vendor/assets with time of last local
|
271
|
+
# precompile. Return true if there have been any changes since the last precompile that
|
272
|
+
# wouldn't be included in the last precompile.
|
273
|
+
#
|
274
|
+
#local_asset_dirs.any?
|
275
|
+
skip_local_precompile?
|
276
|
+
end
|
277
|
+
|
278
|
+
task :precompile_locally do
|
279
|
+
run_locally "time bundle exec rake assets:precompile RAILS_ENV=production RAILS_GROUPS=assets"
|
280
|
+
after_local_precompile
|
281
|
+
end
|
282
|
+
|
283
|
+
task :after_local_precompile do
|
284
|
+
unless keep_public_assets_dir
|
285
|
+
latest_dir = "public/assets.#{Time.now.strftime("%Y-%m-%dT%H.%M.%S")}"
|
286
|
+
# Move them out of the way locally, so that in development it won't try to use (possibly stale) assets from public/assets instead of dynamically generating them
|
287
|
+
run_locally "mv public/assets #{latest_dir}"
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
task :precompile, :roles => :app, :except => { :no_release => false } do
|
292
|
+
if local_asset_current?
|
293
|
+
# We'll just reuse the assets that we've already previously precompiled locally.
|
294
|
+
#latest_dir = local_asset_dirs.last or raise "local_asset_dirs empty"
|
295
|
+
puts "Using existing local asset dir: #{local_asset_dirs.last}"
|
296
|
+
else
|
297
|
+
precompile_locally
|
298
|
+
end
|
299
|
+
#Pathname(latest_dir).exist? or raise "local assets dir #{latest_dir.inspect} does not exist"
|
300
|
+
|
301
|
+
push_changed_assets
|
302
|
+
|
303
|
+
# This is so that the deploy will be aborted if the previous steps failed to produce
|
304
|
+
# asset files in the target directory on the server.
|
305
|
+
run "ls #{release_or_current_path}/public/assets/*.js"
|
306
|
+
end
|
307
|
+
|
308
|
+
else
|
309
|
+
# Just use the standard recipe supplied with capistrano in 'deploy/assets'
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end # if asset_pipeline
|
316
|
+
end
|