app-deployer 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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