alcapon 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/LICENSE.md +7 -0
  2. data/README.md +9 -0
  3. data/bin/capezit +201 -0
  4. data/lib/capez.rb +219 -0
  5. data/lib/db.rb +67 -0
  6. metadata +84 -0
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2012 Arnaud Lafon, http://github.com/alafon
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,9 @@
1
+ # AlCapON : Enable Capistrano for your eZ Publish installations
2
+
3
+ AlCapON is a simple recipe for Capistrano, the well-known deployment toolbox. It helps you dealing with simple task such as pushing your code to your webserver(s), clearing cache, etc.
4
+
5
+ IMPORTANT: this package is currently under development, please consider testing it on a preproduction environment before going further. Please also read the "Known bugs" section carefully.
6
+
7
+ ## Requirements, installation & co
8
+
9
+ Please see [alafon.github.com/alcapon](http://alafon.github.com/alcapon)
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'fileutils'
5
+
6
+ alcapon_path = 'extension/alcapon'
7
+ FileUtils.mkdir_p(File.join(alcapon_path,'config/deploy'))
8
+
9
+ OptionParser.new do |opts|
10
+ opts.banner = "Usage: #{File.basename($0)} [path]"
11
+
12
+ opts.on("-h", "--help", "Displays this help info") do
13
+ puts opts
14
+ exit 0
15
+ end
16
+
17
+ begin
18
+ opts.parse!(ARGV)
19
+ rescue OptionParser::ParseError => e
20
+ warn e.message
21
+ puts opts
22
+ exit 1
23
+ end
24
+ end
25
+
26
+ if ARGV.empty?
27
+ abort "Please specify the directory to capifony, e.g. `#{File.basename($0)} .'"
28
+ elsif !File.exists?(ARGV.first)
29
+ abort "`#{ARGV.first}' does not exist."
30
+ elsif !File.directory?(ARGV.first)
31
+ abort "`#{ARGV.first}' is not a directory."
32
+ elsif ARGV.length > 1
33
+ abort "Too many arguments; please specify only the directory to capify."
34
+ end
35
+
36
+ def unindent(string)
37
+ indentation = string[/\A\s*/]
38
+ string.strip.gsub(/^#{indentation}/, "")
39
+ end
40
+
41
+ files = {
42
+ "Capfile" => unindent(<<-FILE),
43
+ set :alcapon_path, \"#{alcapon_path}\"
44
+ load 'deploy' if respond_to?(:namespace) # cap2 differentiator
45
+ Dir['vendor/gems/*/recipes/*.rb','vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
46
+
47
+ load Gem.find_files('capez.rb').last.to_s
48
+
49
+ set :stage_dir, '#{alcapon_path}/config/deploy'
50
+ Dir['#{alcapon_path}/config/*.rb'].each { |recipe| load(recipe) }
51
+ FILE
52
+
53
+ "#{alcapon_path}/config/deploy.rb" => unindent(<<-FILE),
54
+ # Comment this if you don't want to use a multistage setup
55
+ set :stages, %w(devel production )
56
+ set :default_stage, "devel"
57
+ require 'capistrano/ext/multistage'
58
+
59
+ set :application, "myapp"
60
+ set :repository, "git@github.com:username/myapp.git"
61
+
62
+ set :deploy_to, "/var/www/\#{application}"
63
+ # The user connecting your server through ssh
64
+ set :user, "deploy"
65
+
66
+ # The default branch used (can be overridden on multistage setup)
67
+ set :branch, "master"
68
+
69
+ # Need if you want to deploy somewhere where sudo is needed
70
+ default_run_options[:pty] = true
71
+
72
+ # Use this to use your ssh keys
73
+ # (you might need to run ssh-add /path/to/your/deploy_key before)
74
+ ssh_options[:forward_agent] = true
75
+ # Or if you want to use a specific key
76
+ #ssh_options[:keys] = %w(/home/username/.ssh/id_rsa)
77
+
78
+ # Or: `accurev`, `bzr`, `cvs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none`
79
+ set :scm, :git
80
+
81
+ # Comment this if you don't use submodules
82
+ set :git_enable_submodules, 1
83
+
84
+ # Prevents you from cloning the whole rep. each deploy but your remote servers
85
+ # must be able to get connected to your scm server
86
+ set :deploy_via, :remote_cache
87
+
88
+ # Your Primary HTTP server
89
+ # (Use config/deploy/*.rb files instead if you need a multisage setup)
90
+ role :web, "domain.com", :primary => true
91
+ # Another HTTP server, for instanced when using a clustered mode
92
+ #role :web, "server2"
93
+
94
+ #role :db, "your primary db-server here", :primary => true # This is where Rails migrations will run
95
+ #role :db, "your slave db-server here"
96
+ FILE
97
+
98
+ "#{alcapon_path}/config/ezpublish.rb" => unindent(<<-FILE),
99
+ # This file contains eZ Publish adjustable variables depending on your custom setup
100
+
101
+ # Your webserver user and group, used to chmod directories just after deploy:setup
102
+ set :webserver_user, "apache"
103
+ set :webserver_group, "apache"
104
+
105
+ # If true, will always turn your webserver offline
106
+ # Requires a specific rewrite rule (see documentation)
107
+ set :always_turnoff, false
108
+
109
+ # Array taking all the values given by php bin/php/ezcache.php --list-tags
110
+ #
111
+ # If you want to clear all caches use :
112
+ #set :cache_list, "all"
113
+ set :cache_list, [ "template", "ini", "content" ]
114
+
115
+ # If true, adds '--purge' to the ezcache.php command
116
+ # Be careful with that one, some versions are known to completely delete
117
+ # your var directory (2012.5 for instance)
118
+ set :cache_purge, false
119
+
120
+ # Siteaccess list, needed for setup & shared directory sync
121
+ #set :siteaccess_list, [ "ezflow_site" ]
122
+
123
+ # Set this to tell Capistrano with which host you want to sync your local storage dir
124
+ #set :shared_host, "domain.com"
125
+
126
+ # Which autoloads to generate. By default, regenerates extensions and
127
+ # kernel-override autoloads
128
+ # Possible values : see bin/php/ezpgenerateautoloads.php --help
129
+ set :autoload_list, [ "extension", "kernel-override" ]
130
+
131
+ # TODO : use yml files to manage database credentials securely
132
+ # See http://www.simonecarletti.com/blog/2009/06/capistrano-and-database-yml/
133
+ set :database_uname, "dbuname"
134
+ set :database_passd, "dbpasswd"
135
+ set :database_name, "dbname"
136
+
137
+ # Not implemented
138
+ set :ezpublish_separated_core, false
139
+ set :ezpublish_base, "community"
140
+ set :ezpublish_version, "2012.5"
141
+
142
+ # Check-list (used by cap setup:check)
143
+ depend :remote, :command, "php"
144
+ depend :remote, :match, "php -r \\"echo(version_compare(PHP_VERSION,'5.2.14')?'ok':'ko');\\"", "ok"
145
+ depend :remote, :match, "php -m | grep curl", "curl"
146
+
147
+ # TODO
148
+ #Check : PHP memory_limit >= 128
149
+ #Check : PHP date.imezone = "something"
150
+ #Check : eZ Components (must be bundled if eZ Publish >= 20??.? since there's a patch for Archive)
151
+ #Check : If deploy_via remote_cache => check of the remote servers have access to the scm
152
+ FILE
153
+
154
+ "#{alcapon_path}/config/deploy/devel.rb" => unindent(<<-FILE),
155
+ # There you can override default settings for this specific environment
156
+
157
+ set :branch, "dev"
158
+ #role :web, "myapp.devserv", :primary => true # Your Primary HTTP server
159
+
160
+ # If you need sudo commands to be run with -u sudouser
161
+ #set :admin_runner, "sudouser"
162
+
163
+ # This is used for permissions related tasks
164
+ #set :webserver_user, "www-data"
165
+ #set :webserver_group, "www-data"
166
+ FILE
167
+
168
+ "#{alcapon_path}/config/deploy/production.rb" => unindent(<<-FILE)
169
+ # There you can override default settings for this specific environment
170
+
171
+ set :branch, "master"
172
+ #role :web, "domain.com", :primary => true # Your Primary HTTP server
173
+
174
+ # If you need sudo commands to be run with -u sudouser
175
+ #set :admin_runner, "sudouser"
176
+
177
+ # This is used for permissions related tasks
178
+ #set :webserver_user, "apache"
179
+ #set :webserver_group, "apache"
180
+ FILE
181
+ }
182
+
183
+ base = ARGV.shift
184
+
185
+ files.each do |file, content|
186
+ file = File.join(base, file)
187
+ if File.exists?(file)
188
+ warn "[skip] '#{file}' already exists"
189
+ elsif File.exists?(file.downcase)
190
+ warn "[skip] '#{file.downcase}' exists, which could conflict with `#{file}'"
191
+ else
192
+ unless File.exists?(File.dirname(file))
193
+ puts "[add] making directory '#{File.dirname(file)}'"
194
+ FileUtils.mkdir(File.dirname(file))
195
+ end
196
+ puts "[add] writing '#{file}'"
197
+ File.open(file, "w") { |f| f.write(content) }
198
+ end
199
+ end
200
+
201
+ puts "[done] Your eZ Publish project is now controlled by AlCapON !"
@@ -0,0 +1,219 @@
1
+ load_paths.push File.expand_path('../', __FILE__)
2
+ load 'db.rb'
3
+
4
+ # This will simply do chmod g+w on all dir
5
+ # See task :setup
6
+ set :group_writable, true
7
+
8
+ after "deploy:setup", :roles => :web do
9
+ capez.var.init_shared
10
+ end
11
+
12
+ after "deploy:update", :roles => :web do
13
+ # We don't need to clear the cache anymore but a warmup might be needed
14
+ #capez.cache.clear
15
+ end
16
+
17
+ before "deploy", :roles => :web do
18
+ capez.dev.local_check
19
+ deploy.web.disable
20
+ end
21
+
22
+ after "deploy", :roles => :web do
23
+ deploy.web.enable
24
+ end
25
+
26
+ namespace :deploy do
27
+
28
+ desc <<-DESC
29
+ Finalize the update by creating symlink var -> shared/var
30
+ DESC
31
+ task :finalize_update do
32
+ capez.var.link
33
+ capez.autoloads.generate
34
+ end
35
+
36
+ namespace :web do
37
+ desc <<-DESC
38
+ Puts a html file somewhere in the documentroot
39
+ This file is displayed by a RewriteRule if it exists
40
+ DESC
41
+ task :disable do
42
+ end
43
+
44
+ desc <<-DESC
45
+ Remove the html file so that the application is reachable
46
+ DESC
47
+ task :enable do
48
+ end
49
+ end
50
+ # End of namespace :deploy:web
51
+
52
+ end
53
+
54
+ namespace :capez do
55
+
56
+ namespace :cache do
57
+ desc <<-DESC
58
+ Clear caches the way it is configured in ezpublish.rb
59
+ DESC
60
+ # Caches are just cleared for the primary server
61
+ # Multiple server platform are supposed to use a cluster configuration (eZDFS/eZDBFS)
62
+ # and cache management is done via expiry.php which is managed by the cluster API
63
+ task :clear, :roles => :web, :only => { :primary => true } do
64
+ on_rollback do
65
+ clear
66
+ end
67
+ cache_list.each { |cache_tag| capture "cd #{current_path} && sudo -u #{webserver_user} php bin/php/ezcache.php --clear-tag=#{cache_tag}#{' --purge' if cache_purge}" }
68
+ end
69
+ end
70
+
71
+ namespace :var do
72
+ desc <<-DESC
73
+ Creates the needed folder within your remote(s) var directories
74
+ DESC
75
+ task :init_shared, :roles => :web do
76
+ run( "mkdir -p #{shared_path}/var/storage" )
77
+ siteaccess_list.each{ |siteaccess_identifier|
78
+ run( "mkdir -p #{shared_path}/var/#{siteaccess_identifier}/storage" )
79
+ }
80
+
81
+ fix_permissions( "#{shared_path}/var", webserver_user, webserver_group )
82
+ end
83
+
84
+ desc <<-DESC
85
+ Link .../shared/var into ../releases/[latest_release]/var
86
+ DESC
87
+ task :link, :roles => :web do
88
+ run( "mkdir #{latest_release}/var" )
89
+ siteaccess_list.each{ |siteaccess_identifier|
90
+ run( "mkdir #{latest_release}/var/#{siteaccess_identifier}" )
91
+ }
92
+
93
+ fix_permissions( "#{latest_release}/var", webserver_user, webserver_group )
94
+
95
+ try_sudo( "ln -s #{shared_path}/var/storage #{latest_release}/var/storage", :as => webserver_user )
96
+ siteaccess_list.each{ |siteaccess_identifier|
97
+ try_sudo( "ln -s #{shared_path}/var/#{siteaccess_identifier}/storage #{latest_release}/var/#{siteaccess_identifier}/storage", :as => webserver_user )
98
+ }
99
+ end
100
+
101
+ desc <<-DESC
102
+ Sync your var directory with a remote one
103
+ DESC
104
+ task :sync, :roles => :web, :only => { :primary => true } do
105
+ confirmation = Capistrano::CLI.ui.ask "You're about to sync your local var/ directory with a remote one (current stage = #{stage}). Are you sure (y/N) ?"
106
+ abort "Aborted" unless confirmation.downcase == 'y'
107
+
108
+ shared_host = fetch( :shared_host, nil )
109
+ abort "Please set 'shared_host'" if shared_host == nil
110
+
111
+ # TODO : make it configurable
112
+ exclude_string = ""
113
+ exclude_paths = [ "/cache", "/log", "/*/cache", "/*/log", "/autoload" ]
114
+ exclude_paths.each{ |item|
115
+ exclude_string << "--exclude '#{item}' "
116
+ }
117
+
118
+ run_locally( "rsync -az #{exclude_string} #{user}@#{shared_host}:#{shared_path}/var/* var/" )
119
+ end
120
+
121
+ end
122
+ # End of namespace :capez:var
123
+
124
+ # TODO : cache management must be aware of cluster setup namespace :autoloads do
125
+ namespace :autoloads do
126
+ desc <<-DESC
127
+ Generates autoloads (extensions and kernel overrides)
128
+ DESC
129
+ task :generate do
130
+ on_rollback do
131
+ generate
132
+ end
133
+ autoload_list.each { |autoload|
134
+ capture( "cd #{latest_release} && sudo -u #{webserver_user} php bin/php/ezpgenerateautoloads.php --#{autoload}" )
135
+ }
136
+ end
137
+ end
138
+ # End of namespace :capez:autoloads
139
+
140
+ # Should be transformed in a simple function (not aimed to be called as a Cap task...)
141
+ namespace :dev do
142
+ desc <<-DESC
143
+ Checks if there are local changes or not (only with Git)
144
+ Considers that your main git repo is at the top of your eZ Publish install
145
+ If changes are detected, then ask the user to continue or not
146
+ DESC
147
+ task :local_check do
148
+ if "#{scm}" != "git" then
149
+ abort "Feature only available with git"
150
+ end
151
+
152
+ ezroot_path = fetch( :ezpublish_path, false )
153
+ abort "Please set a correct path to your eZ Publish root (:ezpublish_path) or add 'set :ezpublish_path, File.expand_path( File.dirname( __FILE__ ) )' in your Capfile" unless ezroot_path != false and File.exists?(ezroot_path)
154
+
155
+ git_status = git_status_result( ezroot_path )
156
+
157
+ ask_to_abort = false
158
+ puts "Checking your local git..."
159
+ if git_status['has_local_changes']
160
+ ask_to_abort = true
161
+ puts "You have local changes"
162
+ end
163
+ if git_status['has_new_files']
164
+ ask_to_abort = true
165
+ puts "You have new files"
166
+ end
167
+
168
+ if ask_to_abort
169
+ user_abort = Capistrano::CLI.ui.ask "Abort ? y/n (n)"
170
+ abort "Deployment aborted to commit/add local changes" unless user_abort == "n" or user_abort == ""
171
+ end
172
+
173
+ if git_status['tracked_branch_status'] == 'ahead'
174
+ puts "You have #{git_status['tracked_branch_commits']} commits that need to be pushed"
175
+ push_before = Capistrano::CLI.ui.ask "Push them before deployment ? y/n (y)"
176
+ if push_before == "" or push_before == "y"
177
+ system "git push"
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ def git_status_result(path)
184
+ result = Hash.new
185
+ result['has_local_changes'] = false
186
+ result['has_new_files'] = false
187
+ result['tracked_branch'] = nil
188
+ result['tracked_branch_status'] = nil
189
+ result['tracked_branch_commits'] = 0
190
+ cmd_result = `cd #{path} && git status 2> /dev/null`
191
+ result['raw_result'] = cmd_result
192
+ cmd_result_array = cmd_result.split( /\n/ );
193
+ cmd_result_array.each { |value|
194
+ case value
195
+ when /# Changes not staged for commit:/
196
+ result['has_local_changes'] = true
197
+ when /# Untracked files:/
198
+ result['has_new_files'] = true
199
+ when /# On branch (.*)$/
200
+ result['branch'] = $1
201
+ when /# Your branch is (.*) of '(.*)' by (.*) commits?/
202
+ result['tracked_branch_status'] = $1
203
+ result['tracked_branch'] = $2
204
+ result['tracked_branch_commits'] = $3
205
+ end
206
+ }
207
+ return result
208
+ end
209
+
210
+ def fix_permissions(path,userid,groupid)
211
+ # If admin_runner is not null then make sure that the command is not run with -u admin_runner
212
+ if fetch( :admin_runner, nil ) != nil
213
+ run( "sudo chown -R #{userid}:#{groupid} #{path}" )
214
+ else
215
+ try_sudo( "chown -R #{userid}:#{groupid} #{path}" )
216
+ end
217
+ end
218
+
219
+ end
@@ -0,0 +1,67 @@
1
+ namespace :db do
2
+ desc <<-DESC
3
+ Loads one of the backup made with the db:export:remote task
4
+ DESC
5
+ task :import_to_local do
6
+ confirmation = Capistrano::CLI.ui.ask "You are about to replace your local database by a remote backup (selected stage = #{stage}). Are you sure ? y/n (n)"
7
+ if confirmation == "n" or confirmation == ""
8
+ abort "Aborted"
9
+ end
10
+ backup_dir = File.join( get_backup_dir, "#{stage}" )
11
+ backup_list = `ls #{backup_dir}`
12
+ backup_files = backup_list.split( /\n/ );
13
+ files = Hash.new
14
+ i = 1
15
+ backup_files.each { |value|
16
+ puts "#{i}: #{value}"
17
+ files[i] = value
18
+ i += 1
19
+ }
20
+ file_to_import = Capistrano::CLI.ui.ask "Which one ?"
21
+ if files.has_key?( file_to_import.to_i )
22
+ filename = File.join( backup_dir, files[file_to_import.to_i] )
23
+ system( "gunzip < #{filename} | mysql -u#{database_uname} -p#{database_passd} #{database_name} ")
24
+ else
25
+ abort "Bad index"
26
+ end
27
+ end
28
+
29
+ # Should use :db as :roles
30
+ desc <<-DESC
31
+ Creates a backup from a remote database server
32
+ DESC
33
+ task :backup, :roles => :db do
34
+ filename = generate_backup_name
35
+ file = File.join( "/tmp", filename )
36
+ on_rollback do
37
+ run "rm #{file}"
38
+ end
39
+ run "mysqldump -u#{database_uname} -p#{database_passd} #{database_name} | gzip > #{file}"
40
+ backup_dir_for_this_stage = File.join( get_backup_dir, "#{stage}" )
41
+ create_backup_dir( backup_dir_for_this_stage )
42
+ get file, File.join( backup_dir_for_this_stage, filename )
43
+ run "rm #{file}"
44
+ end
45
+
46
+ desc <<-DESC
47
+ Create a backup from your local instance
48
+ DESC
49
+ task :backup_local do
50
+ backup_dir = File.join( get_backup_dir, 'local' )
51
+ create_backup_dir( backup_dir )
52
+ filename = File.join( backup_dir, generate_backup_name )
53
+ system("mysqldump -u#{database_uname} -p#{database_passd} #{database_name} | gzip -9 > #{filename}")
54
+ end
55
+
56
+ def generate_backup_name
57
+ return "#{Time.now.strftime '%Y-%m%d-%H%M%S'}.sql.gz"
58
+ end
59
+
60
+ def create_backup_dir( path )
61
+ FileUtils.mkdir_p( path )
62
+ end
63
+
64
+ def get_backup_dir
65
+ return "extension/alcapon/backups/database"
66
+ end
67
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: alcapon
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Arnaud Lafon
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-07-10 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: capistrano
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 63
29
+ segments:
30
+ - 2
31
+ - 12
32
+ - 0
33
+ version: 2.12.0
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ description: Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH. This package gives you some tools to deploy your eZ Publish projects.
37
+ email: alcapon@arnaudlafon.com
38
+ executables:
39
+ - capezit
40
+ extensions: []
41
+
42
+ extra_rdoc_files: []
43
+
44
+ files:
45
+ - bin/capezit
46
+ - lib/capez.rb
47
+ - lib/db.rb
48
+ - README.md
49
+ - LICENSE.md
50
+ homepage: http://alafon.github.com/alcapon
51
+ licenses: []
52
+
53
+ post_install_message:
54
+ rdoc_options: []
55
+
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ hash: 3
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ requirements: []
77
+
78
+ rubyforge_project:
79
+ rubygems_version: 1.8.24
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: Enable Capistrano for your eZ Publish projects
83
+ test_files: []
84
+