app-deploy 0.6.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/CHANGES ADDED
@@ -0,0 +1,7 @@
1
+ = app-deploy changes history
2
+
3
+ == app-deploy 0.6.0 / 2009-12-14
4
+ * first release to gemcutter
5
+
6
+ == app-deploy ? / 2008-12-10
7
+ * bundle as gem
data/README ADDED
@@ -0,0 +1,85 @@
1
+ = app-deploy 0.6
2
+ by Lin Jen-Shin (aka godfat-真常[http://godfat.org])
3
+ godfat (XD) godfat.org
4
+
5
+ == LINKS:
6
+
7
+ * github-project[http://github.com/godfat/app-deploy]
8
+
9
+ == DESCRIPTION:
10
+
11
+ rake tasks for deployment
12
+
13
+ == FEATURES:
14
+
15
+ $ rake -T app
16
+ (in /home/photos)
17
+ rake app:daemon:restart[config] # restart the daemon cluster
18
+ rake app:daemon:start[config] # start the daemon cluster
19
+ rake app:daemon:stop[config] # stop the daemon cluster
20
+ rake app:deploy # deploy to master state
21
+ rake app:deploy:after # after deploy hook for you to override
22
+ rake app:deploy:before # before deploy hook for you to override
23
+ rake app:gem:install # install gems
24
+ rake app:gem:reinstall # reinstall gems
25
+ rake app:gem:uninstall # uninstall gems
26
+ rake app:git[cmd] # generic git cmd walk through all dependency
27
+ rake app:git:clone # clone repoitory from github
28
+ rake app:git:pull # pull anything from origin
29
+ rake app:git:stash # make anything reflect master state
30
+ rake app:git:submodule # init and update submodule
31
+ rake app:install # install this application
32
+ rake app:install:after # after install hook for you to override
33
+ rake app:install:before # before install hook for you to override
34
+ rake app:install:remote[hosts,git,cd,branch,script] # remote installation
35
+ rake app:install:remote:setup[user,file,hosts,script] # upload a tarball and untar to user home, then useradd
36
+ rake app:install:remote:sh[hosts,script] # invoke a shell script on remote machines
37
+ rake app:install:remote:upload[file,hosts,path] # upload a file to remote machines
38
+ rake app:install:remote:useradd[user,hosts,script] # create a user on remote machines
39
+ rake app:nginx:reload # reload config
40
+ rake app:nginx:restart # restart nginx
41
+ rake app:nginx:start[config,nginx] # start nginx, default config is config/nginx.conf, nginx is /usr/sbin/nginx
42
+ rake app:nginx:stop[timeout] # stop nginx
43
+ rake app:rack:restart[config] # restart the rack cluster
44
+ rake app:rack:start[config] # start the rack cluster
45
+ rake app:rack:stop[config] # stop the rack cluster
46
+ rake app:server:restart # please define your server:restart task
47
+
48
+ == SYNOPSIS:
49
+
50
+ see
51
+ - example/Rakefile
52
+ - example/rack_cluster.yaml
53
+ - example/daemon_cluster.yaml
54
+
55
+ == REQUIREMENTS:
56
+
57
+ * ruby 1.8.7+
58
+ * [required] rake
59
+ * [optional] rubygems for rubygems task
60
+ * [optional] git for git task
61
+ * [optional] nginx for nginx task
62
+ * [optional] rack and daemons for rack-cluster task
63
+ * [optional] thin for thin task
64
+
65
+ == INSTALL:
66
+
67
+ * gem install app-deploy
68
+
69
+ == LICENSE:
70
+
71
+ Apache License 2.0
72
+
73
+ Copyright (c) 2008-2009, Lin Jen-Shin (aka godfat 真常)
74
+
75
+ Licensed under the Apache License, Version 2.0 (the "License");
76
+ you may not use this file except in compliance with the License.
77
+ You may obtain a copy of the License at
78
+
79
+ http://www.apache.org/licenses/LICENSE-2.0
80
+
81
+ Unless required by applicable law or agreed to in writing, software
82
+ distributed under the License is distributed on an "AS IS" BASIS,
83
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
84
+ See the License for the specific language governing permissions and
85
+ limitations under the License.
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ begin
4
+ require 'bones'
5
+ rescue LoadError
6
+ abort '### Please install the "bones" gem ###'
7
+ end
8
+
9
+ ensure_in_path 'lib'
10
+ proj = 'app-deploy'
11
+ require "#{proj}/version"
12
+
13
+ Bones{
14
+ version AppDeploy::VERSION
15
+
16
+ # ruby_opts [''] # silence warning, too many in addressable and/or dm-core
17
+
18
+ name proj
19
+ url "http://github.com/godfat/#{proj}"
20
+ authors 'Lin Jen-Shin (aka godfat 真常)'
21
+ email 'godfat (XD) godfat.org'
22
+
23
+ history_file 'CHANGES'
24
+ readme_file 'README'
25
+ ignore_file '.gitignore'
26
+ rdoc.include ['\w+']
27
+ }
28
+
29
+ CLEAN.include Dir['**/*.rbc']
30
+
31
+ task :default do
32
+ Rake.application.options.show_task_pattern = /./
33
+ Rake.application.display_tasks_and_comments
34
+ end
data/TODO ADDED
@@ -0,0 +1,10 @@
1
+ = app-deploy todo list
2
+
3
+ * adding sending signal to cluster
4
+
5
+ * escape \" and \' in options?
6
+ * extract rack-cluster
7
+ * detect binding to port succeeded or not
8
+ * fix default options for rack-cluster
9
+
10
+ * provide a way to hide tasks we don't care
@@ -0,0 +1,34 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{app-deploy}
5
+ s.version = "0.5.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Lin Jen-Shin (a.k.a. godfat \347\234\237\345\270\270)"]
9
+ s.date = %q{2009-02-09}
10
+ s.description = %q{rake tasks for deployment}
11
+ s.email = %q{godfat (XD) godfat.org}
12
+ s.extra_rdoc_files = ["CHANGES", "README", "TODO", "app-deploy.gemspec", "example/daemon_cluster.yaml", "example/rack_cluster.yaml", "lib/app-deploy/daemon.rake", "lib/app-deploy/deploy.rake", "lib/app-deploy/gem.rake", "lib/app-deploy/git.rake", "lib/app-deploy/install.rake", "lib/app-deploy/merb.rake", "lib/app-deploy/mongrel.rake", "lib/app-deploy/nginx.rake", "lib/app-deploy/rack.rake", "lib/app-deploy/server.rake", "lib/app-deploy/thin.rake"]
13
+ s.files = ["CHANGES", "README", "Rakefile", "TODO", "app-deploy.gemspec", "example/Rakefile", "example/daemon_cluster.yaml", "example/rack_cluster.yaml", "lib/app-deploy.rb", "lib/app-deploy/daemon.rake", "lib/app-deploy/daemon.rb", "lib/app-deploy/daemon_cluster.rb", "lib/app-deploy/deploy.rake", "lib/app-deploy/gem.rake", "lib/app-deploy/git.rake", "lib/app-deploy/install.rake", "lib/app-deploy/merb.rake", "lib/app-deploy/mongrel.rake", "lib/app-deploy/nginx.rake", "lib/app-deploy/rack.rake", "lib/app-deploy/rack_cluster.rb", "lib/app-deploy/server.rake", "lib/app-deploy/thin.rake", "lib/app-deploy/utils.rb", "lib/app-deploy/version.rb"]
14
+ s.has_rdoc = true
15
+ s.homepage = %q{http://github.com/godfat/app-deploy}
16
+ s.rdoc_options = ["--diagram", "--charset=utf-8", "--inline-source", "--line-numbers", "--promiscuous", "--main", "README"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{ludy}
19
+ s.rubygems_version = %q{1.3.1}
20
+ s.summary = %q{rake tasks for deployment}
21
+
22
+ if s.respond_to? :specification_version then
23
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
+ s.specification_version = 2
25
+
26
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
+ s.add_development_dependency(%q<bones>, [">= 2.4.0"])
28
+ else
29
+ s.add_dependency(%q<bones>, [">= 2.4.0"])
30
+ end
31
+ else
32
+ s.add_dependency(%q<bones>, [">= 2.4.0"])
33
+ end
34
+ end
data/example/Rakefile ADDED
@@ -0,0 +1,113 @@
1
+
2
+ begin
3
+ require 'app-deploy'
4
+ rescue LoadError
5
+ puts "app-deploy not found, automaticly downloading and installing..."
6
+
7
+ gem_cmd = "#{Gem.ruby} #{`which gem`.strip}"
8
+ gem_opt = '--user-install --no-ri --no-rdoc'
9
+ gem_int = "#{gem_cmd} install #{gem_opt}"
10
+
11
+ sh "#{gem_int} bones" # need bones to build app-deploy
12
+
13
+ sh 'git clone git://github.com/godfat/app-deploy.git app-deploy'
14
+ Dir.chdir 'app-deploy'
15
+
16
+ sh 'rake clobber'
17
+ sh 'rake gem:package'
18
+ sh "#{gem_int} --local pkg/app-deploy-*.gem"
19
+
20
+ Dir.chdir '..'
21
+ Gem.refresh
22
+ require 'app-deploy'
23
+ end
24
+
25
+ task :default do
26
+ Rake.application.options.show_task_pattern = /./
27
+ Rake.application.display_tasks_and_comments
28
+ end
29
+
30
+ # install gem from rubyforge
31
+ AppDeploy.dependency_gem :gem => 'bones'
32
+
33
+ # install gem from github
34
+ AppDeploy.dependency_gem :gem => 'pagfiy', :source => 'http://gems.github.com'
35
+
36
+ # clone and build and install gem from github with bones
37
+ AppDeploy.dependency_gem :github_user => 'godfat',
38
+ :github_project => 'friendly_format',
39
+ :task_gem => 'bones'
40
+
41
+ # same as above but with hoe, and select a git clone path.
42
+ AppDeploy.dependency_gem :github_user => 'godfat',
43
+ :github_project => 'mogilefs-client',
44
+ :task_gem => 'hoe',
45
+ :git_path => 'tmp/mogilefs-client'
46
+
47
+ # same as above but with custom install script
48
+ AppDeploy.dependency_gem :github_user => 'godfat',
49
+ :github_project => 'app-deploy',
50
+ :git_path => 'tmp/app-deploy' do
51
+ sh 'rake clobber'
52
+ sh 'rake gem:package'
53
+ sh 'gem install pkg/app-deploy-*.gem'
54
+ end
55
+
56
+ # no gem to install, just clone it
57
+ AppDeploy.dependency :github_user => 'godfat',
58
+ :github_project => 'in_place_editing',
59
+ :git_path => 'tmp/vendor/plugins/in_place_editing'
60
+
61
+ %w[ rack rails daemons rmagick passenger
62
+ dm-core dm-types dm-timestamps dm-aggregates
63
+ mogilefs-client memcache-client rubycas-client ].each{ |name|
64
+ AppDeploy.dependency_gem(:gem => name)
65
+ }
66
+
67
+ namespace :app do
68
+ namespace :install do
69
+ # before install hook
70
+ task :before do
71
+ # explicit install gem, ignore gem that was already installed
72
+ AppDeploy.install_gem(:gem => 'hoe')
73
+ end
74
+
75
+ # after install hook
76
+ task :after do
77
+ # explicit uninstall gem, ignore gem that was not installed
78
+ AppDeploy.uninstall_gem(:gem => 'zzz')
79
+
80
+ # install passenger nginx module
81
+ sh("#{Gem.user_dir}/bin/" +
82
+ Gem.default_exec_format % 'passenger-install-nginx-module' +
83
+ " --auto --auto-download --prefix=#{Dir.pwd}/nginx")
84
+ end
85
+ end
86
+
87
+ namespace :server do
88
+ # use thin as server
89
+ task :restart => 'thin:restart'
90
+
91
+ [:start, :stop, :restart, :reload].each{ |cmd|
92
+ desc "#{cmd} nginx(passenger)"
93
+ task cmd do
94
+ ENV['config'] = '/home/app/config/nginx.conf'
95
+ ENV['nginx'] = '/home/app/nginx/sbin/nginx'
96
+ Rake::Task["app:nginx:#{cmd}"].invoke
97
+ end
98
+ }
99
+ end
100
+ end
101
+
102
+ begin # prevent lacking rails to stop rake app:install to install rails
103
+ gem 'rails'
104
+
105
+ require(File.join(File.dirname(__FILE__), 'config', 'boot'))
106
+
107
+ require 'rake'
108
+ require 'rake/testtask'
109
+ require 'rake/rdoctask'
110
+
111
+ require 'tasks/rails'
112
+ rescue LoadError
113
+ end
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+
3
+ rake app:install
4
+ ./bin/start.sh
@@ -0,0 +1,9 @@
1
+ #!/bin/sh
2
+
3
+ rake app:install:remote:setup user=cas file=ssh.tgz hosts=root@10.0.0.101,root@10.0.0.106,root@10.0.0.111,root@10.0.0.116,root@10.0.0.121
4
+
5
+ rake app:install:remote hosts=cas@10.0.0.101,cas@10.0.0.106,cas@10.0.0.111,cas@10.0.0.116,cas@10.0.0.121 \
6
+ git=git@github.com:godfat/cas.git \
7
+ cd=~ \
8
+ branch=origin/stable \
9
+ script=./bin/install.sh
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+
3
+ rake app:install:remote:sh hosts=cas@10.0.0.101,cas@10.0.0.106,cas@10.0.0.111,cas@10.0.0.116,cas@10.0.0.121 script=./bin/update.sh
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+
3
+ rake app:deploy
@@ -0,0 +1,7 @@
1
+ #!/bin/sh
2
+
3
+ git fetch
4
+ git stash
5
+ git checkout origin/stable
6
+ git submodule update
7
+ ./bin/start.sh
@@ -0,0 +1,10 @@
1
+ ---
2
+ pid: tmp/pids/daemon.pid
3
+ log: log/daemon.log
4
+ chdir: /home/photos
5
+ servers: 5
6
+ user: photos
7
+ group: photos
8
+ ruby: ruby
9
+ script: -I lib daemon/bin/cron.rb
10
+ args: daily
@@ -0,0 +1,13 @@
1
+ ---
2
+ pid: tmp/pids/web_server_rack.pid
3
+ log: log/web_server_rack.log
4
+ port: 3000
5
+ chdir: /home/photos
6
+ host: 0.0.0.0
7
+ servers: 5
8
+ user: photos
9
+ group: photos
10
+ rackup: web_server/config.ru
11
+ server: thin
12
+ environment: none
13
+ delay: 5
@@ -0,0 +1,33 @@
1
+
2
+ require 'app-deploy/daemon_cluster'
3
+
4
+ namespace :app do
5
+ namespace :daemon do
6
+
7
+ desc 'start the daemon cluster'
8
+ task :start, [:config] do |t, args|
9
+ path = args[:config] || 'config/daemon_cluster.yaml'
10
+ AppDeploy::DaemonCluster.each(path){ |config, cmd|
11
+ AppDeploy::DaemonCluster.start(config, cmd)
12
+ }
13
+ end
14
+
15
+ desc 'stop the daemon cluster'
16
+ task :stop, [:config] do |t, args|
17
+ path = args[:config] || 'config/daemon_cluster.yaml'
18
+ AppDeploy::DaemonCluster.each(path){ |config, cmd|
19
+ AppDeploy.term(config[:pid], config[:server])
20
+ }
21
+ end
22
+
23
+ desc 'restart the daemon cluster'
24
+ task :restart, [:config] do |t, args|
25
+ path = args[:config] || 'config/daemon_cluster.yaml'
26
+ AppDeploy::DaemonCluster.each(path){ |config, cmd|
27
+ AppDeploy.term(config[:pid], config[:server])
28
+ AppDeploy::DaemonCluster.start(config, cmd)
29
+ }
30
+ end
31
+
32
+ end # of rack
33
+ end # of app
@@ -0,0 +1,64 @@
1
+
2
+ require 'daemons'
3
+
4
+ require 'etc'
5
+ require 'fileutils'
6
+
7
+ module AppDeploy
8
+
9
+ module Daemon
10
+ module_function
11
+ def daemonize pid_path, log_path, user, group, chdir
12
+ Dir.chdir(chdir) if chdir
13
+
14
+ user ||= Etc.getpwuid(Process.uid).name
15
+ group ||= Etc.getpwuid(Process.gid).name
16
+
17
+ Daemon.change_privilege(user, group)
18
+
19
+ cwd = Dir.pwd
20
+ ::Daemonize.daemonize(log_path)
21
+ Dir.chdir(cwd)
22
+
23
+ Daemon.write_pid(pid_path)
24
+
25
+ at_exit{
26
+ puts "Stopping #{pid_path}..."
27
+ Daemon.remove_pid(pid_path)
28
+ }
29
+ end
30
+
31
+ # extracted from thin
32
+ # Change privileges of the process
33
+ # to the specified user and group.
34
+ def change_privilege user, group
35
+
36
+ uid, gid = Process.euid, Process.egid
37
+ target_uid = Etc.getpwnam(user).uid
38
+ target_gid = Etc.getgrnam(group).gid
39
+
40
+ if uid != target_uid || gid != target_gid
41
+ puts "Changing process privilege to #{user}:#{group}"
42
+
43
+ # Change process ownership
44
+ Process.initgroups(user, target_gid)
45
+ Process::GID.change_privilege(target_gid)
46
+ Process::UID.change_privilege(target_uid)
47
+ end
48
+
49
+ rescue Errno::EPERM => e
50
+ puts "Couldn't change user and group to #{user}:#{group}: #{e}"
51
+ end
52
+
53
+ def write_pid path
54
+ FileUtils.mkdir_p(File.dirname(path))
55
+ File.open(path, 'w'){ |f| f.write(Process.pid) }
56
+ File.chmod(0644, path)
57
+ end
58
+
59
+ def remove_pid path
60
+ File.delete(path) if File.exist?(path)
61
+ end
62
+
63
+ end # of Daemon
64
+ end # of AppDeploy
@@ -0,0 +1,62 @@
1
+
2
+ module AppDeploy
3
+
4
+ module DaemonCluster
5
+ module_function
6
+ def each path
7
+ config_orig = YAML.load(File.read(path)).inject({}){ |h, kv|
8
+ k, v = kv
9
+ h[k.to_sym] = v
10
+ h
11
+ }
12
+
13
+ config_orig = { :servers => 1,
14
+ :log => 'log/daemon_cluster.log',
15
+ :pid => 'tmp/pids/daemon_cluster.pid'
16
+ }.merge(config_orig)
17
+
18
+ config_orig[:servers].times{ |n|
19
+ config = config_orig.dup
20
+
21
+ config[:num] = n
22
+ config[:pid] = DaemonCluster.pid_path(config, n)
23
+ config[:log] = DaemonCluster.log_path(config, n)
24
+
25
+ args = [:pid, :log, :user, :group, :chdir].map{ |kind|
26
+ value = config.send(:[], kind)
27
+ value ? "'#{value}'" : 'nil'
28
+ }.join(', ')
29
+
30
+ init_script = "require 'app-deploy/daemon'; AppDeploy::Daemon.daemonize(#{args});"
31
+ ruby_opts = "-r rubygems -e \"#{init_script} load('#{config[:script]}')\""
32
+
33
+ yield( config, "#{config[:ruby]} #{ruby_opts} #{config[:args]}" )
34
+ }
35
+ end
36
+
37
+ def start config, cmd
38
+ puts "Starting ##{config[:num]} #{config[:ruby]} #{config[:script]} #{config[:args]}..."
39
+ sh cmd
40
+ puts
41
+ end
42
+
43
+ def pid_path config, number
44
+ path, script, args = config[:pid], config[:script], config[:args]
45
+ DaemonCluster.path_with_number(path, script, args, number)
46
+ end
47
+
48
+ def log_path config, number
49
+ # log should expand path since daemons' working dir is different
50
+ path, script, args = config[:log], config[:script], config[:args]
51
+ File.expand_path(DaemonCluster.path_with_number(path, script, args, number))
52
+ end
53
+
54
+ def path_with_number path, script, args, number
55
+ name = (args ? File.basename("#{script} #{args}") :
56
+ File.basename(script) ).gsub(/\s+/, '_')
57
+
58
+ ext = File.extname(path)
59
+ path.gsub(/#{ext}$/, ".#{name}.#{number}#{ext}")
60
+ end
61
+ end # of DaemonCluster
62
+ end # of AppDeploy
@@ -0,0 +1,18 @@
1
+
2
+ namespace :app do
3
+
4
+ desc 'deploy to master state'
5
+ task :deploy => 'deploy:default'
6
+
7
+ namespace :deploy do
8
+ desc 'before deploy hook for you to override'
9
+ task :before
10
+
11
+ desc 'after deploy hook for you to override'
12
+ task :after
13
+
14
+ task :default => [:before, 'git:stash',
15
+ 'server:restart', :after]
16
+
17
+ end # of deploy
18
+ end # of app
@@ -0,0 +1,20 @@
1
+
2
+ namespace :app do
3
+ namespace :merb do
4
+
5
+ desc 'start the merb server, default config: config/merb.yml'
6
+ task :start, [:config] do |t, args|
7
+ sh "merb #{AppDeploy.extract_config(args[:config] || 'config/merb.yml')}"
8
+ end
9
+
10
+ desc 'stop the merb server'
11
+ task :stop do
12
+ sh 'merb -K all'
13
+ sleep(0.5)
14
+ end
15
+
16
+ desc 'restart the merb server'
17
+ task :restart => [:stop, :start]
18
+
19
+ end # of merb
20
+ end # of app
@@ -0,0 +1,13 @@
1
+
2
+ namespace :app do
3
+ namespace :mongrel do
4
+
5
+ [:start, :stop, :restart].each{ |name|
6
+ desc "#{name} the mongrel cluster"
7
+ task name do
8
+ sh "mongrel_rails cluster::#{name}"
9
+ end
10
+ }
11
+
12
+ end # of mongrel
13
+ end # of app
@@ -0,0 +1,25 @@
1
+
2
+ namespace :app do
3
+ namespace :gem do
4
+
5
+ desc 'install gems'
6
+ task :install do
7
+ AppDeploy.each(:gem){ |dep|
8
+ puts "Installing #{dep[:gem] || dep[:github_project]}..."
9
+ AppDeploy.install_gem(dep)
10
+ }
11
+ end
12
+
13
+ desc 'uninstall gems'
14
+ task :uninstall do
15
+ AppDeploy.each(:gem){ |dep|
16
+ puts "Uninstalling #{dep[:gem] || dep[:github_project]}..."
17
+ AppDeploy.uninstall_gem(dep)
18
+ }
19
+ end
20
+
21
+ desc 'reinstall gems'
22
+ task :reinstall => [:uninstall, :install]
23
+
24
+ end # of gem
25
+ end # of app
@@ -0,0 +1,66 @@
1
+
2
+ namespace :app do
3
+
4
+ desc 'generic git cmd walk through all dependency'
5
+ task :git, [:cmd] do |t, args|
6
+ cmd = args[:cmd] || 'status'
7
+
8
+ puts "Invoking git #{cmd}..."
9
+ begin
10
+ sh "git #{cmd}"
11
+ rescue RuntimeError => e
12
+ puts e
13
+ end
14
+
15
+ AppDeploy.each(:github){ |opts|
16
+ puts "Invoking git #{cmd} on #{opts[:github_project]}..."
17
+ begin
18
+ sh "git #{cmd}"
19
+ rescue RuntimeError => e
20
+ puts e
21
+ end
22
+ }
23
+
24
+ end
25
+
26
+ namespace :git do
27
+
28
+ task :reset => :stash
29
+
30
+ desc 'make anything reflect master state'
31
+ task :stash do
32
+ puts 'Stashing...'
33
+ sh 'git stash'
34
+ end
35
+
36
+ desc 'init and update submodule'
37
+ task :submodule do
38
+ sh 'git submodule init'
39
+ sh 'git submodule update'
40
+ end
41
+
42
+ desc 'clone repoitory from github'
43
+ task :clone do
44
+ AppDeploy.github.each{ |dep|
45
+ puts "Cloning #{dep[:github_project]}..."
46
+ AppDeploy.clone(dep)
47
+ }
48
+ end
49
+
50
+ desc 'pull anything from origin'
51
+ task :pull do
52
+ puts 'Pulling...'
53
+ begin
54
+ sh 'git pull' if `git remote` =~ /^origin$/
55
+ rescue RuntimeError => e
56
+ puts e
57
+ end
58
+
59
+ AppDeploy.each(:github){ |opts|
60
+ puts "Pulling #{opts[:github_project]}..."
61
+ sh 'git pull'
62
+ }
63
+ end
64
+
65
+ end # of git
66
+ end # of app
@@ -0,0 +1,84 @@
1
+
2
+ namespace :app do
3
+
4
+ desc 'install this application'
5
+ task :install => 'install:default'
6
+
7
+ namespace :install do
8
+ desc 'before install hook for you to override'
9
+ task :before
10
+
11
+ desc 'after install hook for you to override'
12
+ task :after
13
+
14
+ task :default => [:before,
15
+ 'git:submodule',
16
+ 'git:clone',
17
+ 'gem:install',
18
+ :after]
19
+
20
+ desc 'remote installation'
21
+ task :remote, [:hosts, :git, :cd, :branch, :script] => 'remote:default'
22
+
23
+ namespace :remote do
24
+ task :default, [:hosts, :git, :cd, :branch, :script] do |t, args|
25
+ unless [args[:hosts], args[:git]].all?
26
+ puts 'please fill your arguments like:'
27
+ puts " > rake app:install:remote[#{args.names.join(',').upcase}]"
28
+ exit(1)
29
+ end
30
+
31
+ cd = args[:cd] || '~'
32
+ branch = args[:branch] || 'master'
33
+ tmp = "app-deploy-#{Time.now.to_i}"
34
+
35
+ chdir = "cd #{cd}"
36
+ clone = "git clone #{args[:git]} /tmp/#{tmp}"
37
+ setup = "find /tmp/#{tmp} -maxdepth 1 '!' -name #{tmp} -exec mv -f '{}' #{cd} ';'"
38
+ rmdir = "rmdir /tmp/#{tmp}"
39
+ check = "git checkout #{branch}"
40
+
41
+ ENV['script'] =
42
+ "#{chdir}; #{clone}; #{setup}; #{rmdir}; #{check}; #{args[:script]}"
43
+
44
+ Rake::Task['app:install:remote:sh'].invoke
45
+ end
46
+
47
+ desc 'invoke a shell script on remote machines'
48
+ task :sh, [:hosts, :script] do |t, args|
49
+ args[:hosts].split(',').map{ |host|
50
+ script = args[:script].gsub('"', '\\"')
51
+ Thread.new{ sh "ssh #{host} \"#{script}\"" }
52
+ }.each(&:join)
53
+ end
54
+
55
+ desc 'upload a file to remote machines'
56
+ task :upload, [:file, :hosts, :path] do |t, args|
57
+ args[:hosts].split(',').each{ |host|
58
+ sh "scp #{args[:file]} #{host}:#{args[:path]}"
59
+ }
60
+ end
61
+
62
+ desc 'create a user on remote machines'
63
+ task :useradd, [:user, :hosts, :script] do |t, args|
64
+ useradd = "sudo useradd -m #{args[:user]}"
65
+ args[:hosts].split(',').each{ |host|
66
+ script = "#{useradd}; #{args[:script]}".gsub('"', '\\"')
67
+ sh "ssh #{host} \"#{script}\""
68
+ }
69
+ end
70
+
71
+ desc 'upload a tarball and untar to user home, then useradd'
72
+ task :setup, [:user, :file, :hosts, :script] do |t, args|
73
+ ENV['path'] = "/tmp/app-deploy-#{Time.now.to_i}"
74
+ Rake::Task['app:install:remote:upload'].invoke
75
+
76
+ ENV['script'] = "sudo -u #{args[:user]} tar -zxf #{ENV['path']}" +
77
+ " -C /home/#{args[:user]};" +
78
+ " rm #{ENV['path']}; #{args[:script]}"
79
+ Rake::Task['app:install:remote:useradd'].invoke
80
+ end
81
+
82
+ end # of remote
83
+ end # of install
84
+ end # of app
@@ -0,0 +1,31 @@
1
+
2
+ namespace :app do
3
+ namespace :nginx do
4
+
5
+ desc 'start nginx, default config is config/nginx.conf, nginx is /usr/sbin/nginx'
6
+ task :start, [:config, :nginx] do |t, args|
7
+ # TODO: extract pid file path
8
+ if File.exist?('tmp/pids/nginx.pid')
9
+ puts "WARN: pid file #{'tmp/pids/nginx.pid'} already exists, ignoring."
10
+ else
11
+ sh "#{args[:nginx] || '/usr/sbin/nginx'} -c #{args[:config] || 'config/nginx.conf'}"
12
+ end
13
+ end
14
+
15
+ desc 'reload config'
16
+ task :reload do
17
+ # sh 'kill -HUP `cat tmp/pids/nginx.pid`'
18
+ AppDeploy.hup('tmp/pids/nginx.pid', 'nginx')
19
+ end
20
+
21
+ desc 'stop nginx'
22
+ task :stop, [:timeout] do |t, args|
23
+ # sh "kill -TERM `cat tmp/pids/nginx.pid`"
24
+ AppDeploy.term('tmp/pids/nginx.pid', 'nginx', args[:timeout] || 5)
25
+ end
26
+
27
+ desc 'restart nginx'
28
+ task :restart => [:stop, :start]
29
+
30
+ end # of nginx
31
+ end # of app
@@ -0,0 +1,34 @@
1
+
2
+ require 'app-deploy/rack_cluster'
3
+
4
+ namespace :app do
5
+ namespace :rack do
6
+
7
+ desc 'start the rack cluster'
8
+ task :start, [:config] do |t, args|
9
+ path = args[:config] || 'config/rack_cluster.yaml'
10
+ AppDeploy::RackCluster.each(path){ |config, ruby_opts, rack_opts|
11
+ AppDeploy::RackCluster.start(config, ruby_opts, rack_opts)
12
+ }
13
+ end
14
+
15
+ desc 'stop the rack cluster'
16
+ task :stop, [:config] do |t, args|
17
+ path = args[:config] || 'config/rack_cluster.yaml'
18
+ AppDeploy::RackCluster.each(path){ |config, ruby_opts, rack_opts|
19
+ AppDeploy.term(config[:pid], config[:server])
20
+ }
21
+ end
22
+
23
+ desc 'restart the rack cluster'
24
+ task :restart, :config do |t, args|
25
+ path = args[:config] || 'config/rack_cluster.yaml'
26
+ AppDeploy::RackCluster.each(path){ |config, ruby_opts, rack_opts|
27
+ AppDeploy.term(config[:pid], config[:server])
28
+ AppDeploy::RackCluster.start(config, ruby_opts, rack_opts)
29
+ sleep(config[:delay]) if config[:delay]
30
+ }
31
+ end
32
+
33
+ end # of rack
34
+ end # of app
@@ -0,0 +1,79 @@
1
+
2
+ module AppDeploy
3
+
4
+ module RackCluster
5
+ module_function
6
+ def each path
7
+ config_orig = {}
8
+ rack_opts = AppDeploy.extract_config(path){ |opt, value|
9
+ case opt
10
+ when 'environment'
11
+ "--env #{value}"
12
+
13
+ when *%w[server host]
14
+ config_orig[opt.to_sym] = value
15
+ "--#{opt} #{value}"
16
+
17
+ when *%w[user group chdir servers require rackup daemonize port pid log delay]
18
+ config_orig[opt.to_sym] = value
19
+ nil # rack doesn't have this option
20
+
21
+ else
22
+ "--#{opt} #{value}"
23
+
24
+ end
25
+ }
26
+
27
+ config_orig = { :servers => 1,
28
+ :port => 9292,
29
+ :host => '0.0.0.0',
30
+ :log => 'log/rack_cluster.log',
31
+ :pid => 'tmp/pids/rack_cluster.pid',
32
+ :server => 'mongrel',
33
+ :rackup => 'config.ru' }.merge(config_orig)
34
+
35
+ config_orig[:servers].times{ |n|
36
+ config = config_orig.dup
37
+
38
+ config[:port] += n
39
+ config[:pid] = RackCluster.pid_path(config[:pid], config[:port])
40
+ config[:log] = RackCluster.log_path(config[:log], config[:port])
41
+
42
+ args = [:pid, :log, :user, :group, :chdir].map{ |kind|
43
+ value = config.send(:[], kind)
44
+ value ? "'#{value}'" : 'nil'
45
+ }.join(', ')
46
+
47
+ init_script = "AppDeploy::Daemon.daemonize(#{args})"
48
+ ruby_opts = "-r rubygems -r app-deploy/daemon -e \"#{init_script}\""
49
+
50
+ yield( config, ruby_opts, rack_opts + " --port #{config[:port]}" )
51
+ }
52
+
53
+ end
54
+
55
+ def start config, ruby_opts, rack_opts
56
+ puts "Starting #{config[:server]} on #{config[:host]}:#{config[:port]}..."
57
+ sh "rackup #{ruby_opts} #{rack_opts} #{config[:rackup]}"
58
+ puts
59
+ end
60
+
61
+ def pid_path path, port
62
+ RackCluster.path_with_number(path, port)
63
+ end
64
+
65
+ def log_path path, port
66
+ # log should expand path since daemons' working dir is different
67
+ File.expand_path(RackCluster.path_with_number(path, port))
68
+ end
69
+
70
+ # extracted from thin
71
+ # Add the server port or number in the filename
72
+ # so each instance get its own file
73
+ def path_with_number path, number
74
+ ext = File.extname(path)
75
+ path.gsub(/#{ext}$/, ".#{number}#{ext}")
76
+ end
77
+
78
+ end # of RackCluster
79
+ end # of AppDeploy
@@ -0,0 +1,9 @@
1
+
2
+ namespace :app do
3
+ namespace :server do
4
+
5
+ desc 'please define your server:restart task'
6
+ task :restart
7
+
8
+ end # of server
9
+ end # of app
@@ -0,0 +1,30 @@
1
+
2
+ namespace :app do
3
+ namespace :signal do
4
+
5
+ desc 'invoke command if not running already.'
6
+ task :start, [:script, :pid] do |t, args|
7
+ if File.exist?(args[:pid])
8
+ puts "WARN: pid file #{args[:pid]} already exists, ignoring."
9
+ else
10
+ sh args[:script]
11
+ end
12
+ end
13
+
14
+ desc 'send a signal to a process with a pid file'
15
+ task :kill, [:signal] do |t, args|
16
+ # sh 'kill -HUP `cat tmp/pids/nginx.pid`'
17
+ AppDeploy.kill(args[:signal], 'tmp/pids/nginx.pid', 'nginx')
18
+ end
19
+
20
+ desc 'stop nginx'
21
+ task :stop, [:timeout] do |t, args|
22
+ # sh "kill -TERM `cat tmp/pids/nginx.pid`"
23
+ AppDeploy.term('tmp/pids/nginx.pid', 'nginx', args[:timeout] || 5)
24
+ end
25
+
26
+ desc 'restart nginx'
27
+ task :restart => [:stop, :start]
28
+
29
+ end # of nginx
30
+ end # of app
@@ -0,0 +1,13 @@
1
+
2
+ namespace :app do
3
+ namespace :thin do
4
+
5
+ [:start, :stop, :restart].each{ |name|
6
+ desc "#{name} the thin cluster"
7
+ task name, [:config] do |t, args|
8
+ sh "thin -C #{args[:config] || 'config/thin.yml'} #{name}"
9
+ end
10
+ }
11
+
12
+ end # of thin
13
+ end # of app
@@ -0,0 +1,189 @@
1
+
2
+ module AppDeploy
3
+ module_function
4
+ def github; @github ||= []; end
5
+ def dependency opts = {}
6
+ opts = opts.dup
7
+ opts[:git_path] ||= opts[:github_project]
8
+ github << opts.freeze
9
+ end
10
+
11
+ def gem; @gem ||= []; end
12
+ def dependency_gem opts = {}, &block
13
+ opts = opts.dup
14
+
15
+ if opts[:github_project]
16
+ opts[:git_path] ||= opts[:github_project]
17
+ opts[:task_gem] = block if block_given?
18
+ github << opts.freeze
19
+ end
20
+
21
+ gem << opts.freeze
22
+ end
23
+
24
+ def each *with
25
+ # yield common gem first, github gem last
26
+ with = [:gem, :github] if with.empty?
27
+ cwd = Dir.pwd
28
+
29
+ # github's gem would be in @github and @gem,
30
+ # so call uniq to ensure it wouldn't get called twice.
31
+ with.map{ |kind| AppDeploy.send(kind) }.flatten.uniq.each{ |opts|
32
+ puts
33
+
34
+ if opts[:github_project]
35
+ if File.directory?(opts[:git_path])
36
+ begin
37
+ Dir.chdir(opts[:git_path])
38
+ yield(opts)
39
+ rescue RuntimeError => e
40
+ puts e
41
+ ensure
42
+ Dir.chdir(cwd)
43
+ end
44
+ else
45
+ puts "Skip #{opts[:github_project]}, because it was not found"
46
+ end
47
+ else # it's a plain gem
48
+ yield(opts)
49
+ end
50
+ }
51
+ end
52
+
53
+ def extract_config config
54
+ require 'yaml'
55
+ YAML.load(File.read(config)).inject([]){ |result, opt_value|
56
+ opt, value = opt_value
57
+ if block_given?
58
+ result << yield(opt, value)
59
+ else
60
+ result << "--#{opt} #{value}"
61
+ end
62
+ result
63
+ }.compact.join(' ')
64
+ end
65
+
66
+ # about git
67
+ def clone opts
68
+ user, proj, path = opts[:github_user], opts[:github_project], opts[:git_path]
69
+
70
+ if File.exist?(path)
71
+ puts "Skip #{proj} because #{path} exists"
72
+ else
73
+ sh "git clone git://github.com/#{user}/#{proj}.git #{path}"
74
+ sh "git --git-dir #{path}/.git gc"
75
+ end
76
+ end
77
+
78
+ # about gem
79
+ def installed_gem? gem_name
80
+ # `#{gem_bin} list '^#{gem_name}$'` =~ /^#{gem_name}/
81
+ Gem.send(:gem, gem_name)
82
+ true
83
+ rescue LoadError
84
+ false
85
+ end
86
+
87
+ def install_gem opts
88
+ gem_name = opts[:gem] || opts[:github_project]
89
+
90
+ if AppDeploy.installed_gem?(gem_name)
91
+ puts "Skip #{gem_name} because it was installed. Uninstall first if you want to reinstall"
92
+
93
+ else
94
+ if opts[:gem]
95
+ AppDeploy.install_gem_remote(opts[:gem], opts[:source])
96
+ else
97
+ AppDeploy.install_gem_local(opts[:github_project], opts[:task_gem])
98
+ end
99
+
100
+ end
101
+ end
102
+
103
+ def uninstall_gem opts
104
+ gem_name = opts[:gem] || opts[:github_project]
105
+ if AppDeploy.installed_gem?(gem_name)
106
+ sh "#{gem_bin} uninstall #{gem_name}"
107
+ else
108
+ puts "Skip #{gem_name} because it was not installed"
109
+ end
110
+ end
111
+
112
+ def install_gem_remote gem_name, source = nil
113
+ sh "#{gem_bin} install #{gem_name}#{source ? ' --source ' + source : ''}"
114
+ end
115
+
116
+ def install_gem_local proj, task
117
+ case task
118
+ when 'bones'
119
+ sh 'rake clobber'
120
+ sh 'rake gem:package'
121
+ sh "#{gem_bin} install --local pkg/#{proj}-*.gem --no-ri --no-rdoc"
122
+
123
+ when 'hoe'
124
+ sh 'rake gem'
125
+ sh "#{gem_bin} install --local pkg/#{proj}-*.gem --no-ri --no-rdoc"
126
+
127
+ when Proc
128
+ task.call
129
+ end
130
+ end
131
+
132
+ def gem_bin
133
+ Gem.ruby + ' ' + `which gem`.strip
134
+ end
135
+
136
+ # about sending signal
137
+ def read_pid pid_path
138
+ if File.exist?(pid_path)
139
+ File.read(pid_path).strip.to_i
140
+
141
+ else
142
+ puts "WARN: No pid file found in #{pid_path}"
143
+ nil
144
+
145
+ end
146
+ end
147
+
148
+ def term pid_path, name = nil, limit = 5
149
+ if pid = AppDeploy.read_pid(pid_path)
150
+ puts "Sending TERM to #{name}(#{pid})..."
151
+
152
+ else
153
+ return
154
+
155
+ end
156
+
157
+ require 'timeout'
158
+ begin
159
+ timeout(limit){
160
+ while true
161
+ Process.kill('TERM', pid)
162
+ sleep(0.1)
163
+ end
164
+ }
165
+ rescue Errno::ESRCH
166
+ if File.exist?(pid_path)
167
+ puts "WARN: No such pid: #{pid}, removing #{pid_path}..."
168
+ File.delete(pid_path)
169
+ else
170
+ puts "Killed #{name}(#{pid})"
171
+ end
172
+
173
+ rescue Timeout::Error
174
+ puts "Timeout(#{limit}) killing #{name}(#{pid})"
175
+
176
+ end
177
+ end
178
+
179
+ def hup pid_path, name = nil
180
+ if pid = AppDeploy.read_pid(pid_path)
181
+ puts "Sending HUP to #{name}(#{pid})..."
182
+ Process.kill('HUP', pid)
183
+ end
184
+ rescue Errno::ESRCH
185
+ puts "WARN: No such pid: #{pid}, removing #{pid_path}..."
186
+ File.delete(pid_path)
187
+ end
188
+
189
+ end
@@ -0,0 +1,4 @@
1
+
2
+ module AppDeploy
3
+ VERSION = '0.6.0'
4
+ end
data/lib/app-deploy.rb ADDED
@@ -0,0 +1,6 @@
1
+
2
+ %w[ deploy gem git install nginx server rack daemon].each{ |task|
3
+ load "app-deploy/#{task}.rake"
4
+ }
5
+
6
+ require 'app-deploy/utils'
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: app-deploy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.0
5
+ platform: ruby
6
+ authors:
7
+ - "Lin Jen-Shin (aka godfat \xE7\x9C\x9F\xE5\xB8\xB8)"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-14 00:00:00 +08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: bones
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 3.2.0
24
+ version:
25
+ description: " rake tasks for deployment"
26
+ email: godfat (XD) godfat.org
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - CHANGES
33
+ - README
34
+ - Rakefile
35
+ - TODO
36
+ - app-deploy.gemspec
37
+ - example/Rakefile
38
+ - example/bin/install.sh
39
+ - example/bin/remote_install.sh
40
+ - example/bin/remote_update.sh
41
+ - example/bin/start.sh
42
+ - example/bin/update.sh
43
+ - example/daemon_cluster.yaml
44
+ - example/rack_cluster.yaml
45
+ - lib/app-deploy/daemon.rake
46
+ - lib/app-deploy/deploy.rake
47
+ - lib/app-deploy/deprecated/merb.rake
48
+ - lib/app-deploy/deprecated/mongrel.rake
49
+ - lib/app-deploy/gem.rake
50
+ - lib/app-deploy/git.rake
51
+ - lib/app-deploy/install.rake
52
+ - lib/app-deploy/nginx.rake
53
+ - lib/app-deploy/rack.rake
54
+ - lib/app-deploy/server.rake
55
+ - lib/app-deploy/signal.rake
56
+ - lib/app-deploy/thin.rake
57
+ files:
58
+ - CHANGES
59
+ - README
60
+ - Rakefile
61
+ - TODO
62
+ - app-deploy.gemspec
63
+ - example/Rakefile
64
+ - example/bin/install.sh
65
+ - example/bin/remote_install.sh
66
+ - example/bin/remote_update.sh
67
+ - example/bin/start.sh
68
+ - example/bin/update.sh
69
+ - example/daemon_cluster.yaml
70
+ - example/rack_cluster.yaml
71
+ - lib/app-deploy.rb
72
+ - lib/app-deploy/daemon.rake
73
+ - lib/app-deploy/daemon.rb
74
+ - lib/app-deploy/daemon_cluster.rb
75
+ - lib/app-deploy/deploy.rake
76
+ - lib/app-deploy/deprecated/merb.rake
77
+ - lib/app-deploy/deprecated/mongrel.rake
78
+ - lib/app-deploy/gem.rake
79
+ - lib/app-deploy/git.rake
80
+ - lib/app-deploy/install.rake
81
+ - lib/app-deploy/nginx.rake
82
+ - lib/app-deploy/rack.rake
83
+ - lib/app-deploy/rack_cluster.rb
84
+ - lib/app-deploy/server.rake
85
+ - lib/app-deploy/signal.rake
86
+ - lib/app-deploy/thin.rake
87
+ - lib/app-deploy/utils.rb
88
+ - lib/app-deploy/version.rb
89
+ has_rdoc: true
90
+ homepage: http://github.com/godfat/app-deploy
91
+ licenses: []
92
+
93
+ post_install_message:
94
+ rdoc_options:
95
+ - --main
96
+ - README
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: "0"
104
+ version:
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: "0"
110
+ version:
111
+ requirements: []
112
+
113
+ rubyforge_project: app-deploy
114
+ rubygems_version: 1.3.5
115
+ signing_key:
116
+ specification_version: 3
117
+ summary: rake tasks for deployment
118
+ test_files: []
119
+