app-deployer 0.0.3

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.
@@ -0,0 +1,14 @@
1
+ <?php
2
+ class DATABASE_CONFIG {
3
+ public $default = array(
4
+ 'datasource' => '<%= datasource %>',
5
+ 'persistent' => '<%= persistent %>',
6
+ 'host' => '<%= host %>',
7
+ 'login' => '<%= login %>',
8
+ 'password' => '<%= password %>',
9
+ 'database' => '<%= database %>',
10
+ 'prefix' => '<%= prefix %>',
11
+ 'encoding' => '<%= encoding %>'
12
+ );
13
+ }
14
+ ?>
@@ -0,0 +1,213 @@
1
+ Capistrano::Configuration.instance(:must_exist).load do
2
+
3
+ require 'erb'
4
+
5
+ # =========================================================================
6
+ # Settings
7
+ # =========================================================================
8
+
9
+ _cset :shared_app_dirs, []
10
+ _cset(:lithium_repo) { "https://github.com/UnionOfRAD/lithium.git" }
11
+ _cset(:lithium_branch) { "master" }
12
+ _cset :shared_children, %w(libraries tmp)
13
+ _cset :tmp_children, %w(cache logs tests)
14
+ _cset :cache_children, %w(templates)
15
+ _cset :logs_files, %w(debug error)
16
+ _cset(:database_folder) { File.join(shared_path, "config/bootstrap") }
17
+ _cset(:database_path) { File.join("#{database_folder}", "connections.php") }
18
+ _cset(:tmp_path) { File.join(shared_path, "tmp") }
19
+ _cset(:cache_path) { File.join(tmp_path, "cache") }
20
+ _cset(:logs_path) { File.join(tmp_path, "logs") }
21
+
22
+ # =========================================================================
23
+ # Hooks
24
+ # =========================================================================
25
+
26
+ after('deploy:setup', 'lithium:setup')
27
+ after('deploy:create_symlink', 'lithium:create_symlink')
28
+
29
+ ["composer:install", "composer:update"].each do |action|
30
+ before action do
31
+ if copy_vendors
32
+ composer.copy_vendors
33
+ end
34
+ end
35
+ end
36
+
37
+ after "deploy:finalize_update" do
38
+ if use_composer
39
+ if update_vendors
40
+ composer.update
41
+ else
42
+ composer.install
43
+ end
44
+ end
45
+ if clear_cache
46
+ lithium.cache.clear
47
+ end
48
+ end
49
+
50
+ # =========================================================================
51
+ # Tasks
52
+ # =========================================================================
53
+
54
+ namespace :lithium do
55
+
56
+ desc <<-DESC
57
+ Prepares server for deployment of a lithium application. \
58
+
59
+ By default, it will create a shallow clone of the lithium repository \
60
+ inside #{shared_path}/libraries/lithium and run deploy:lithium:update.
61
+ DESC
62
+ task :setup do
63
+ transaction do
64
+ unless use_composer
65
+ run "cd #{shared_path}/libraries && git clone --depth 1 #{lithium_repo} lithium"
66
+ checkout
67
+ end
68
+ connections.setup
69
+ shared.setup
70
+ end
71
+ end
72
+
73
+ desc <<-DESC
74
+ Force lithium installation to checkout a new branch/tag.
75
+ DESC
76
+ task :checkout do
77
+ on_rollback { run "rm -rf #{shared_path}/lithium; true" }
78
+ stream "cd #{shared_path}/lithium && git checkout -q #{lithium_branch}"
79
+ end
80
+
81
+ desc <<-DESC
82
+ Update the lithium repository to the latest version.
83
+ DESC
84
+ task :update do
85
+ stream "cd #{shared_path}/libraries/lithium && git pull"
86
+ end
87
+
88
+ desc <<-DESC
89
+ This is a task that will get called from the deploy:create_symlink task
90
+ It runs just before the release is symlinked to the current directory
91
+
92
+ You should use it to create symlinks to things like your database config \
93
+ and any shared directories or files that your app uses
94
+ DESC
95
+ task :create_symlink do
96
+ transaction do
97
+ connections.create_symlink
98
+ shared.create_symlink
99
+ end
100
+ end
101
+
102
+ # Framework specific tasks
103
+
104
+ # Caching
105
+ namespace :cache do
106
+ desc <<-DESC
107
+ Clears cache and sub-directories.
108
+
109
+ Recursively finds all files in :cache_path and runs `rm -f` on each. If a file \
110
+ is renamed/removed after it was found but before it removes it, no error \
111
+ will prompt (-ignore_readdir_race). If symlinks are found, they will not be followed
112
+
113
+ You will rarely need to call this task directly; instead, use the `deploy' \
114
+ task (which performs a complete deploy, including `lithium:cache:clear')
115
+ DESC
116
+ task :clear, :roles => :web, :except => { :no_release => true } do
117
+ run "#{try_sudo} find -P #{cache_path} -ignore_readdir_race -type f -name '*' -exec rm -f {} \\;"
118
+ end
119
+ end
120
+
121
+ # Connections config
122
+ namespace :connections do
123
+ desc <<-DESC
124
+ Generates lithium connections file in #{shared_path}/config/bootstrap/ \
125
+ and symlinks #{current_path}/config/bootstrap/connections.php to it
126
+ DESC
127
+ task :setup, :roles => :web, :except => { :no_release => true } do
128
+ on_rollback { run "rm -f #{database_path}; true" }
129
+ puts "Connections setup"
130
+
131
+ prompt_with_default(:type, "database|MongoDb|http")
132
+ case type
133
+ when 'database'
134
+ prompt_with_default(:login, user)
135
+ set :password, Capistrano::CLI.password_prompt("password:")
136
+ prompt_with_default(:encoding, 'UTF-8')
137
+ end
138
+
139
+ prompt_with_default(:host, "127.0.0.1")
140
+ prompt_with_default(:database, application)
141
+
142
+ template = File.read(File.join(File.dirname(__FILE__), "templates", "connections.php.erb"))
143
+ result = ERB.new(template).result(binding)
144
+
145
+ run "#{try_sudo} mkdir -p #{database_folder}"
146
+ put(result, "#{database_path}", :mode => 0644, :via => :scp)
147
+ end
148
+
149
+ desc <<-DESC
150
+ Symlinks the connections file.
151
+ DESC
152
+ task :create_symlink, :roles => :web, :except => { :no_release => true } do
153
+ run "#{try_sudo} ln -s #{database_path} #{current_path}/config/bootstrap/connections.php"
154
+ end
155
+ end
156
+
157
+ # Shared directories and files
158
+ namespace :shared do
159
+ desc <<-DESC
160
+ Creates shared folders on the server
161
+ DESC
162
+ task :setup do
163
+ dirs = [deploy_to, releases_path, shared_path]
164
+ dirs += shared_children.map { |d| File.join(shared_path, d) }
165
+ tmp_dirs = tmp_children.map { |d| File.join(tmp_path, d) }
166
+ tmp_dirs += cache_children.map { |d| File.join(cache_path, d) }
167
+ run "echo #{dirs} && #{try_sudo} mkdir -p #{(dirs + tmp_dirs).join(' ')} && #{try_sudo} chmod -R 777 #{tmp_path}" if (!user.empty?)
168
+
169
+ if shared_app_dirs
170
+ shared_app_dirs.each { | link | run "#{try_sudo} mkdir -p #{shared_path}/#{link}" }
171
+ end
172
+ end
173
+
174
+ desc <<-DESC
175
+ Symlinks all shared files and folders
176
+ DESC
177
+ task :create_symlink do
178
+ run "ln -s #{shared_path}/tmp #{latest_release}/resources/tmp";
179
+ if shared_app_dirs
180
+ shared_app_dirs.each { | link | run "ln -nfs #{shared_path}/#{link} #{current_path}/#{link}" }
181
+ end
182
+ end
183
+ end
184
+
185
+ # Logs
186
+ namespace :log do
187
+ desc <<-DESC
188
+ Clears logs and sub-directories
189
+
190
+ Recursively finds all files in :logs_path and runs `rm -f` on each. If a file \
191
+ is renamed/removed after it was found but before it removes it, no error \
192
+ will prompt (-ignore_readdir_race). If symlinks are found, they will not be followed
193
+ DESC
194
+ task :clear, :roles => :web, :except => { :no_release => true } do
195
+ run "#{try_sudo} find -P #{logs_path} -ignore_readdir_race -type f -name '*' -exec rm -f {} \\;"
196
+ end
197
+
198
+ desc <<-DESC
199
+ Streams the result of `tail -f` on all :logs_files \
200
+
201
+ By default, the files are `debug` and `error`. You can add your own \
202
+ in config/deploy.rb
203
+
204
+ set :logs_files %w(debug error my_log_file)
205
+ DESC
206
+ task :tail, :roles => :web, :except => { :no_release => true } do
207
+ files = logs_files.map { |d| File.join(logs_path, d) }
208
+ stream "#{try_sudo} tail -f #{files.join(' ')}"
209
+ end
210
+ end
211
+
212
+ end
213
+ end
@@ -0,0 +1,64 @@
1
+ <?php
2
+ /**
3
+ * Lithium: the most rad php framework
4
+ *
5
+ * @copyright Copyright 2012, Union of RAD (http://union-of-rad.org)
6
+ * @license http://opensource.org/licenses/bsd-license.php The BSD License
7
+ */
8
+
9
+ /**
10
+ * ### Configuring backend database connections
11
+ *
12
+ * Lithium supports a wide variety relational and non-relational databases, and is designed to allow
13
+ * and encourage you to take advantage of multiple database technologies, choosing the most optimal
14
+ * one for each task.
15
+ *
16
+ * As with other `Adaptable`-based configurations, each database configuration is defined by a name,
17
+ * and an array of information detailing what database adapter to use, and how to connect to the
18
+ * database server. Unlike when configuring other classes, `Connections` uses two keys to determine
19
+ * which class to select. First is the `'type'` key, which specifies the type of backend to
20
+ * connect to. For relational databases, the type is set to `'database'`. For HTTP-based backends,
21
+ * like CouchDB, the type is `'http'`. Some backends have no type grouping, like MongoDB, which is
22
+ * unique and connects via a custom PECL extension. In this case, the type is set to `'MongoDb'`,
23
+ * and no `'adapter'` key is specified. In other cases, the `'adapter'` key identifies the unique
24
+ * adapter of the given type, i.e. `'MySql'` for the `'database'` type, or `'CouchDb'` for the
25
+ * `'http'` type. Note that while adapters are always specified in CamelCase form, types are
26
+ * specified either in CamelCase form, or in underscored form, depending on whether an `'adapter'`
27
+ * key is specified. See the examples below for more details.
28
+ *
29
+ * ### Multiple environments
30
+ *
31
+ * As with other `Adaptable` classes, `Connections` supports optionally specifying different
32
+ * configurations per named connection, depending on the current environment. For information on
33
+ * specifying environment-based configurations, see the `Environment` class.
34
+ *
35
+ * @see lithium\core\Adaptable
36
+ * @see lithium\core\Environment
37
+ */
38
+ use lithium\data\Connections;
39
+ <% case type
40
+ when 'database' %>
41
+ Connections::add('default', array(
42
+ 'type' => 'database',
43
+ 'adapter' => 'MySql',
44
+ 'host' => '<%= host %>',
45
+ 'login' => '<%= login %>',
46
+ 'password' => '<%= password %>',
47
+ 'database' => '<%= database %>',
48
+ 'encoding' => '<%= encoding %>'
49
+ ));
50
+ <% when 'http' %>
51
+ Connections::add('default', array(
52
+ 'type' => 'http',
53
+ 'adapter' => 'CouchDb',
54
+ 'host' => '<%= host %>',
55
+ 'database' => '<%= database %>'
56
+ ));
57
+ <% else %>
58
+ Connections::add('default', array(
59
+ 'type' => 'MongoDb',
60
+ 'host' => '<%= host %>',
61
+ 'database' => '<%= database %>'
62
+ ));
63
+ <% end %>
64
+ ?>
@@ -0,0 +1,142 @@
1
+ # encoding: utf-8
2
+ Capistrano::Configuration.instance(:must_exist).load do
3
+ # =========================================================================
4
+ # These are helper methods that will be available to your recipes.
5
+ # =========================================================================
6
+
7
+ def _cset(name, *args, &block)
8
+ unless exists?(name)
9
+ set(name, *args, &block)
10
+ end
11
+ end
12
+
13
+ # Asks the shell for a response or else uses the default if nothing
14
+ # is entered
15
+ def prompt_with_default(var, default, &block)
16
+ set(var) do
17
+ Capistrano::CLI.ui.ask("#{var} [#{default}] : ", &block)
18
+ end
19
+ set var, default if eval("#{var.to_s}.empty?")
20
+ end
21
+
22
+ # Check to see if a file exists
23
+ def remote_file_exists?(full_path)
24
+ 'true' == capture("if [ -e #{full_path} ]; then echo 'true'; fi").strip
25
+ end
26
+
27
+ # Auxiliary helper method for the `deploy:check' task. Lets you set up your
28
+ # own dependencies.
29
+ def depend(location, type, *args)
30
+ deps = fetch(:dependencies, {})
31
+ deps[location] ||= {}
32
+ deps[location][type] ||= []
33
+ deps[location][type] << args
34
+ set :dependencies, deps
35
+ end
36
+
37
+ # Temporarily sets an environment variable, yields to a block, and restores
38
+ # the value when it is done.
39
+ def with_env(name, value)
40
+ saved, ENV[name] = ENV[name], value
41
+ yield
42
+ ensure
43
+ ENV[name] = saved
44
+ end
45
+
46
+ # logs the command then executes it locally.
47
+ # returns the command output as a string
48
+ def run_locally(cmd)
49
+ logger.trace "executing locally: #{cmd.inspect}" if logger
50
+ output_on_stdout = nil
51
+ elapsed = Benchmark.realtime do
52
+ output_on_stdout = `#{cmd}`
53
+ end
54
+ if $?.to_i > 0 # $? is command exit code (posix style)
55
+ raise Capistrano::LocalArgumentError, "Command #{cmd} returned status code #{$?}"
56
+ end
57
+ logger.trace "command finished in #{(elapsed * 1000).round}ms" if logger
58
+ output_on_stdout
59
+ end
60
+
61
+ # If a command is given, this will try to execute the given command, as
62
+ # described below. Otherwise, it will return a string for use in embedding in
63
+ # another command, for executing that command as described below.
64
+ #
65
+ # If :run_method is :sudo (or :use_sudo is true), this executes the given command
66
+ # via +sudo+. Otherwise is uses +run+. If :as is given as a key, it will be
67
+ # passed as the user to sudo as, if using sudo. If the :as key is not given,
68
+ # it will default to whatever the value of the :admin_runner variable is,
69
+ # which (by default) is unset.
70
+ #
71
+ # THUS, if you want to try to run something via sudo, and what to use the
72
+ # root user, you'd just to try_sudo('something'). If you wanted to try_sudo as
73
+ # someone else, you'd just do try_sudo('something', :as => "bob"). If you
74
+ # always wanted sudo to run as a particular user, you could do
75
+ # set(:admin_runner, "bob").
76
+ def try_sudo(*args)
77
+ options = args.last.is_a?(Hash) ? args.pop : {}
78
+ command = args.shift
79
+ raise ArgumentError, "too many arguments" if args.any?
80
+
81
+ as = options.fetch(:as, fetch(:admin_runner, nil))
82
+ via = fetch(:run_method, :sudo)
83
+ if command
84
+ invoke_command(command, :via => via, :as => as)
85
+ elsif via == :sudo
86
+ sudo(:as => as)
87
+ else
88
+ ""
89
+ end
90
+ end
91
+
92
+ # Same as sudo, but tries sudo with :as set to the value of the :runner
93
+ # variable (which defaults to "app").
94
+ def try_runner(*args)
95
+ options = args.last.is_a?(Hash) ? args.pop : {}
96
+ args << options.merge(:as => fetch(:runner, "app"))
97
+ try_sudo(*args)
98
+ end
99
+
100
+
101
+ def pretty_print(msg)
102
+ if logger.level == Capistrano::Logger::IMPORTANT
103
+ pretty_errors
104
+
105
+ msg = msg.slice(0, 57)
106
+ msg << '.' * (60 - msg.size)
107
+ print msg
108
+ else
109
+ puts msg.green
110
+ end
111
+ end
112
+
113
+ def puts_ok
114
+ if logger.level == Capistrano::Logger::IMPORTANT && !$error
115
+ puts '✔'.green
116
+ end
117
+
118
+ $error = false
119
+ end
120
+
121
+ def pretty_errors
122
+ if !$pretty_errors_defined
123
+ $pretty_errors_defined = true
124
+
125
+ class << $stderr
126
+ @@firstLine = true
127
+ alias _write write
128
+
129
+ def write(s)
130
+ if @@firstLine
131
+ s = '✘' << "\n" << s
132
+ @@firstLine = false
133
+ end
134
+
135
+ _write(s.red)
136
+ $error = true
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ end
@@ -0,0 +1,42 @@
1
+ Capistrano::Configuration.instance(:must_exist).load do
2
+ namespace :mysql do
3
+ desc <<-DESC
4
+ Creates MySQL database, database user and grants permissions on DB servers
5
+ DESC
6
+ task :create, :roles => :db, :except => { :no_releases => true } do
7
+ require 'erb'
8
+ prompt_with_default(:mysql_admin_user, 'root')
9
+ _cset :mysql_admin_password, Capistrano::CLI.password_prompt("password:")
10
+ prompt_with_default(:mysql_grant_priv_type, 'ALL')
11
+ prompt_with_default(:mysql_grant_locations, 'localhost')
12
+ prompt_with_default(:db_login, user)
13
+ _cset :db_password, Capistrano::CLI.password_prompt("password:")
14
+ prompt_with_default(:db_name, application)
15
+ prompt_with_default(:db_encoding, 'utf8')
16
+
17
+ set :tmp_filename, File.join(shared_path, "config/create_db_#{db_name}.sql")
18
+
19
+ template = File.read(File.join(File.dirname(__FILE__), "../templates", "create_database.sql.erb"))
20
+ result = ERB.new(template).result(binding)
21
+
22
+ put(result, "#{tmp_filename}", :mode => 0644, :via => :scp)
23
+
24
+ run "mysql -u #{mysql_admin_user} -p#{mysql_admin_password} < #{tmp_filename}"
25
+ run "#{try_sudo} rm #{tmp_filename}"
26
+ end
27
+
28
+ desc <<-DESC
29
+ Exports MySQL database and copies it to the shared directory
30
+ DESC
31
+ task :export, :roles => :db, :except => { :no_releases => true } do
32
+ prompt_with_default(:mysql_admin_user, 'root')
33
+ _cset :mysql_admin_password, Capistrano::CLI.password_prompt("password:")
34
+ database = Capistrano::CLI.ui.ask("Which database should we export: ")
35
+ timestamp = Time.now.utc.strftime("%Y%m%d%H%M%S")
36
+ run "mysqldump -u #{mysql_admin_user} -p #{mysql_admin_password} > #{database}-#{timestamp}.sql"
37
+ download "#{database}-#{timestamp}.sql", "~/#{database}-#{timestamp}.sql"
38
+ logger.info "Database dump has been downloaded to ~/#{database}-#{timestamp}.sql"
39
+ run "rm #{database}-#{timestamp}.sql"
40
+ end
41
+ end
42
+ end