fast_git_deploy 0.2.1

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 ADDED
@@ -0,0 +1 @@
1
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'capistrano'
4
+
5
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,29 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ fast_git_deploy (0.2.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ capistrano (2.13.5)
10
+ highline
11
+ net-scp (>= 1.0.0)
12
+ net-sftp (>= 2.0.0)
13
+ net-ssh (>= 2.0.14)
14
+ net-ssh-gateway (>= 1.1.0)
15
+ highline (1.6.15)
16
+ net-scp (1.0.4)
17
+ net-ssh (>= 1.99.1)
18
+ net-sftp (2.0.5)
19
+ net-ssh (>= 2.0.9)
20
+ net-ssh (2.6.2)
21
+ net-ssh-gateway (1.1.0)
22
+ net-ssh (>= 1.99.1)
23
+
24
+ PLATFORMS
25
+ ruby
26
+
27
+ DEPENDENCIES
28
+ capistrano
29
+ fast_git_deploy!
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2009-2013 Scott Taylor
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,36 @@
1
+ = fast_git_deploy
2
+
3
+ Before (with fast_remote_cache deploy strategy):
4
+
5
+ $ time cap staging deploy:update
6
+
7
+ ...
8
+
9
+ real 1m56.811s
10
+ user 0m0.560s
11
+ sys 0m0.118s
12
+
13
+
14
+ After:
15
+
16
+ $ time cap staging deploy:update
17
+
18
+ ...
19
+
20
+ real 0m19.987s
21
+ user 0m0.538s
22
+ sys 0m0.110s
23
+
24
+ == Install it into your rails app:
25
+
26
+ gem 'fast_git_deploy'
27
+
28
+ == Switch an existing project:
29
+
30
+ cap deploy:warm
31
+
32
+ == Setup a new project:
33
+
34
+ cap deploy:setup
35
+ cap deploy:cold
36
+ cap deploy
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'fast_git_deploy/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "fast_git_deploy"
8
+ gem.version = FastGitDeploy::VERSION::STRING
9
+ gem.authors = ["Scott Taylor"]
10
+ gem.email = ["scott@railsnewbie.com"]
11
+ gem.description = %q{The Fast Git Deploy Method - just a git reset --hard to update code}
12
+ gem.summary = <<-HERE
13
+ Amazingly fast git deploys by using only a current directory (using git as the version the control history).
14
+
15
+ It's the same technique github uses at their company.
16
+ HERE
17
+ gem.homepage = "http://github.com/smtlaissezfaire/fast_git_deploy"
18
+ gem.files = `git ls-files`.split($/)
19
+ gem.require_paths = ["lib"]
20
+ end
@@ -0,0 +1,15 @@
1
+
2
+ if defined?(Capistrano) &&
3
+ Capistrano::Configuration.respond_to?(:instance) &&
4
+ instance = Capistrano::Configuration.instance
5
+
6
+ require File.dirname(__FILE__) + "/fast_git_deploy/recipes/fast_git_deploy"
7
+ require File.dirname(__FILE__) + "/fast_git_deploy/recipes/fast_git_deploy/rollback"
8
+ require File.dirname(__FILE__) + "/fast_git_deploy/recipes/fast_git_deploy/setup"
9
+
10
+ FastGitDeploy::Main.load_into(instance)
11
+ FastGitDeploy::Rollback.load_into(instance)
12
+ FastGitDeploy::Setup.load_into(instance)
13
+ end
14
+
15
+
@@ -0,0 +1,161 @@
1
+ module FastGitDeploy
2
+ module Main
3
+ def self.load_into(configuration)
4
+ configuration.load do
5
+ current_dir = File.expand_path(File.dirname(__FILE__))
6
+
7
+ set :scm, "git"
8
+ set :scm_command, "git"
9
+ set(:revision_log) { "#{deploy_to}/revisions.log" }
10
+ set(:version_file) { "#{current_path}/REVISION" }
11
+ set :migrate_target, :current
12
+ set :releases, ['current']
13
+ set(:release_path) { File.join(releases_path, "current") }
14
+ set(:releases_path) { File.join(deploy_to) }
15
+
16
+ # set :branch, "master"
17
+
18
+ namespace :deploy do
19
+ desc <<-DESC
20
+ Deploy a "cold" application (deploy the app for the first time).
21
+ DESC
22
+ task :cold do
23
+ fast_git_setup.cold
24
+ end
25
+
26
+ desc <<-DESC
27
+ Deploy a "warm" application - one which is already running, but was
28
+ setup with deploy:cold provided by capistrano's default tasks
29
+ DESC
30
+ task :warm do
31
+ fast_git_setup.warm
32
+ end
33
+
34
+ desc <<-DESC
35
+ Deploy and run pending migrations. This will work similarly to the
36
+ `deploy' task, but will also run any pending migrations (via the
37
+ `deploy:migrate' task) prior to updating the symlink. Note that the
38
+ update in this case it is not atomic, and transactions are not used,
39
+ because migrations are not guaranteed to be reversible.
40
+ DESC
41
+ task :migrations do
42
+ set :migrate_target, :current
43
+ update_code
44
+ symlink
45
+ migrate
46
+ restart
47
+ end
48
+
49
+ desc "Just like deploy:migrations, but puts up the maintenance page while migrating"
50
+ task :long do
51
+ set :migrate_target, :current
52
+ deploy.web.disable
53
+ update_code
54
+ symlink
55
+ migrate
56
+ restart
57
+ deploy.web.enable
58
+ end
59
+
60
+ desc "Updates code in the repos by fetching and resetting to the latest in the branch"
61
+ task :update_code, :except => { :no_release => true } do
62
+ run [
63
+ "cd #{current_path}",
64
+ "#{scm_command} fetch",
65
+ "#{scm_command} reset --hard origin/#{branch}"
66
+ ].join(" && ")
67
+
68
+ set_revisions
69
+ finalize_update
70
+ end
71
+
72
+ desc <<-DESC
73
+ [internal] Touches up the released code. This is called by update_code
74
+ after the basic deploy finishes. It assumes a Rails project was deployed,
75
+ so if you are deploying something else, you may want to override this
76
+ task with your own environment's requirements.
77
+
78
+ This will touch all assets in public/images,
79
+ public/stylesheets, and public/javascripts so that the times are
80
+ consistent (so that asset timestamping works). This touch process
81
+ is only carried out if the :normalize_asset_timestamps variable is
82
+ set to true, which is the default.
83
+ DESC
84
+ task :finalize_update, :except => { :no_release => true } do
85
+ if fetch(:normalize_asset_timestamps, true)
86
+ stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S")
87
+ asset_paths = %w(images stylesheets javascripts).map { |p| "#{current_path}/public/#{p}" }.join(" ")
88
+ run "find #{asset_paths} -exec touch -t #{stamp} {} ';'; true", :env => { "TZ" => "UTC" }
89
+ end
90
+ end
91
+
92
+ desc "Symlink system files & set revision info"
93
+ task :symlink, :except => { :no_release => true } do
94
+ symlink_system_files
95
+ end
96
+
97
+ desc "Symlink system files"
98
+ task :symlink_system_files, :except => { :no_release => true } do
99
+ run [
100
+ "rm -rf #{current_path}/log #{current_path}/public/system #{current_path}/tmp/pids",
101
+ "mkdir -p #{current_path}/public",
102
+ "mkdir -p #{current_path}/tmp",
103
+ "ln -s #{shared_path}/log #{current_path}/log",
104
+ "ln -s #{shared_path}/system #{current_path}/public/system",
105
+ "ln -s #{shared_path}/pids #{current_path}/tmp/pids"
106
+ ].join(" && ")
107
+ end
108
+
109
+ desc "Set the revisions file. This allows us to go back to previous versions."
110
+ task :set_revisions, :except => { :no_release => true } do
111
+ set_version_file
112
+ update_revisions_log
113
+ end
114
+
115
+ task :set_version_file, :except => { :no_release => true } do
116
+ run [
117
+ "cd #{current_path}",
118
+ "#{scm_command} rev-list HEAD | head -n 1 > #{version_file}"
119
+ ].join(" && ")
120
+ end
121
+
122
+ task :update_revisions_log, :except => { :no_release => true } do
123
+ run "echo `date +\"%Y-%m-%d %H:%M:%S\"` $USER $(cat #{version_file}) >> #{deploy_to}/revisions.log"
124
+ end
125
+
126
+ desc "Do nothing (since we have no releases directory)"
127
+ task :cleanup do
128
+ end
129
+
130
+ desc "Do nothing (No need to create a symlink)"
131
+ task :create_symlink do
132
+ end
133
+
134
+ desc <<-DESC
135
+ Prepares one or more servers for deployment. Before you can use any \
136
+ of the Capistrano deployment tasks with your project, you will need to \
137
+ make sure all of your servers have been prepared with `cap deploy:setup'. When \
138
+ you add a new server to your cluster, you can easily run the setup task \
139
+ on just that server by specifying the HOSTS environment variable:
140
+
141
+ $ cap HOSTS=new.server.com deploy:setup
142
+
143
+ It is safe to run this task on servers that have already been set up; it \
144
+ will not destroy any deployed revisions or data.
145
+ DESC
146
+ task :setup, :except => { :no_release => true } do
147
+ dirs = [deploy_to, shared_path]
148
+ dirs += shared_children.map { |d| File.join(shared_path, d) }
149
+ dir_names = dirs.join(' ')
150
+
151
+ run [
152
+ "#{try_sudo} mkdir -p #{dir_names}",
153
+ "#{try_sudo} chown -R #{user}:#{user} #{dir_names}",
154
+ "#{try_sudo} chmod g+w #{dir_names}"
155
+ ].join(" && ")
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,62 @@
1
+ module FastGitDeploy
2
+ module Rollback
3
+ def self.load_into(configuration)
4
+ configuration.load do
5
+ namespace :deploy do
6
+ namespace :rollback do
7
+ desc "Rolls the app back one revision"
8
+ task :code, :except => { :no_release => true } do
9
+ current_revision = capture("cat #{version_file}").gsub(/\r?\n?/, "")
10
+ previous_revision = nil
11
+
12
+ revision_log_data = capture("cat #{revision_log}").split(/\r?\n/)
13
+ revisions = revision_log_data.map do |entry|
14
+ entry.split(" ").last
15
+ end
16
+
17
+ revisions.reverse!
18
+
19
+ revisions.each_with_index do |revision, index|
20
+ if current_revision == revision
21
+ # we have found the currently deployed revision
22
+ # so scan the file backwards until a different revision
23
+ # is found (removing duplicates - i.e.):
24
+ #
25
+ # 2010-04-21 15:22:59 deploy 2a285b0f600c7ed31b307390ad91c
26
+ # 2010-04-22 09:58:28 deploy 53cff5db28116ecd5ad32d11ee6e1
27
+ # 2010-04-22 10:02:41 deploy 53cff5db28116ecd5ad32d11ee6e1
28
+ # 2010-04-26 08:18:39 deploy 5494dc2a00beb5350eff6be151987
29
+ # 2010-04-27 09:10:06 deploy 5494dc2a00beb5350eff6be151987
30
+ # 2010-04-27 11:49:29 deploy 5494dc2a00beb5350eff6be151987
31
+ #
32
+ # Rolling back from 5494dc should yield 53cff5db
33
+ previous_revision = revisions[index..revision_log_data.length-1].detect do |rev|
34
+ rev != current_revision
35
+ end
36
+ end
37
+ end
38
+
39
+ if previous_revision
40
+ run [
41
+ "cd #{current_path}",
42
+ "#{scm_command} reset --hard #{previous_revision}"
43
+ ].join(" && ")
44
+ else
45
+ raise(Capistrano::Error, "Couldn't find a revision previous to #{current_revision}")
46
+ end
47
+ end
48
+
49
+ desc "Rolls back the app one revision, restarts mongrel, and writes the revision to the VERSION file (but not revisions.log)"
50
+ task :default do
51
+ transaction do
52
+ rollback.code
53
+ deploy.restart
54
+ deploy.set_version_file
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,107 @@
1
+ module FastGitDeploy
2
+ module Setup
3
+ def self.load_into(configuration)
4
+ configuration.load do
5
+ namespace :deploy do
6
+ namespace :fast_git_setup do
7
+ task :cold do
8
+ clone_repository
9
+ finalize_clone
10
+ create_revision_log
11
+
12
+ deploy.update
13
+ deploy.start
14
+ end
15
+
16
+ def self.clone_repository_command(path)
17
+ [
18
+ "if [ ! -e #{path} ]",
19
+ "then mkdir -p #{deploy_to}",
20
+ "cd #{deploy_to}",
21
+ "#{scm_command} clone #{repository} #{path}",
22
+ "fi"
23
+ ].join("; ")
24
+ end
25
+
26
+ desc "Clones the repos"
27
+ task :clone_repository, :except => { :no_release => true } do
28
+ run clone_repository_command(current_path)
29
+ end
30
+
31
+ task :create_revision_log, :except => { :no_release => true } do
32
+ run [
33
+ "if [ ! -e #{revision_log} ]",
34
+ "then touch #{revision_log}",
35
+ "chmod 664 #{revision_log}",
36
+ "fi"
37
+ ].join("; ")
38
+ end
39
+
40
+ task :warm do
41
+ clone_repository_to_tmp_path
42
+
43
+ deploy.web.disable
44
+ remove_old_app
45
+ rename_clone
46
+ finalize_clone
47
+ deploy.default
48
+ deploy.web.enable
49
+ end
50
+
51
+ task :clone_repository_to_tmp_path, :except => { :no_release => true } do
52
+ run clone_repository_command("#{current_path}.clone")
53
+ end
54
+
55
+ task :rename_clone, :except => { :no_release => true } do
56
+ run "mv #{current_path}.clone #{current_path}"
57
+ end
58
+
59
+ task :remove_old_app do
60
+ remove_releases
61
+ remove_current
62
+ end
63
+
64
+ task :remove_releases, :except => { :no_release => true } do
65
+ run [
66
+ "if [ -e #{deploy_to}/releases ]",
67
+ "then mv #{deploy_to}/releases #{deploy_to}/releases.old",
68
+ "fi"
69
+ ].join("; ")
70
+ end
71
+
72
+ task :remove_current, :except => { :no_release => true } do
73
+ # test -h => symlink
74
+ run [
75
+ "if [ -h #{current_path} ]",
76
+ "then mv #{current_path} #{current_path}.old",
77
+ "fi"
78
+ ].join("; ")
79
+ end
80
+
81
+
82
+ desc <<-HERE
83
+ This task will make the release group-writable (if the :group_writable
84
+ variable is set to true, which is the default). It will then set up
85
+ symlinks to the shared directory for the log, system, and tmp/pids
86
+ directories.
87
+ HERE
88
+ task :finalize_clone, :except => { :no_release => true } do
89
+ run "chmod -R g+w #{current_path}" if fetch(:group_writable, true)
90
+
91
+ # mkdir -p is making sure that the directories are there for some SCM's that don't
92
+ # save empty folders
93
+ run [
94
+ "rm -rf #{current_path}/log #{current_path}/public/system #{current_path}/tmp/pids",
95
+ "mkdir -p #{current_path}/public",
96
+ "mkdir -p #{current_path}/tmp",
97
+ "ln -s #{shared_path}/log #{current_path}/log",
98
+ "ln -s #{shared_path}/system #{current_path}/public/system",
99
+ "ln -s #{shared_path}/pids #{current_path}/tmp/pids"
100
+ ].join(" && ")
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,9 @@
1
+ module FastGitDeploy
2
+ module VERSION
3
+ MAJOR = 0
4
+ MINOR = 2
5
+ TINY = 1
6
+
7
+ STRING = "#{MAJOR}.#{MINOR}.#{TINY}"
8
+ end
9
+ end
data/spec/Capfile ADDED
@@ -0,0 +1,4 @@
1
+ load 'deploy' if respond_to?(:namespace) # cap2 differentiator
2
+ Dir[File.dirname(__FILE__) + '/../**/recipes/*.rb'].each { |plugin| load(plugin) }
3
+
4
+ load File.dirname(__FILE__) + '/config/deploy.rb' # remove this line to skip loading any of the default tasks
@@ -0,0 +1,35 @@
1
+ def self.join(*args)
2
+ current_path = File.dirname(__FILE__)
3
+ File.expand_path(File.join(current_path, *args))
4
+ end
5
+
6
+ set :application, "fast_git_deploy_test"
7
+ set :repository, join("..", "..", ".git")
8
+ set :deploy_to, join("..", "deployments")
9
+ set :scm_command, `which git`.chomp
10
+ set :user, `whoami`.chomp
11
+
12
+ set :scm, :git
13
+
14
+ ssh_options[:paranoid] = false
15
+ default_run_options[:pty] = true
16
+
17
+ role :web, "127.0.0.1"
18
+ role :app, "127.0.0.1"
19
+ role :db, "127.0.0.1", :primary => true
20
+
21
+ set :branch, "master"
22
+
23
+ namespace :deploy do
24
+ task :restart do
25
+ # do nothing
26
+ end
27
+
28
+ task :migrate do
29
+ # do nothing
30
+ end
31
+
32
+ task :start do
33
+ # do nothing
34
+ end
35
+ end
@@ -0,0 +1,39 @@
1
+ require "spec_helper"
2
+
3
+ describe "fast git deploy" do
4
+ def cap_execute(command)
5
+ commands = [
6
+ "--file", File.expand_path("#{File.dirname(__FILE__)}/Capfile"),
7
+ "--quiet"
8
+ ]
9
+ commands.push command.split(" ")
10
+ commands.flatten!
11
+
12
+ Capistrano::CLI.parse(commands).execute!
13
+ end
14
+
15
+ it "should be able to deploy with a dry-run" do
16
+ cap_execute "-n deploy"
17
+ end
18
+
19
+ it "should be able to deploy:setup" do
20
+ cap_execute "deploy:setup"
21
+ end
22
+
23
+ it "should be able to deploy:cold after deploy:setup" do
24
+ cap_execute "deploy:setup"
25
+ cap_execute "deploy:cold"
26
+ end
27
+
28
+ it "should be able to deploy" do
29
+ cap_execute "deploy:setup"
30
+ cap_execute "deploy:cold"
31
+ cap_execute "deploy"
32
+ end
33
+
34
+ it "should be able to deploy:migrations" do
35
+ cap_execute "deploy:setup"
36
+ cap_execute "deploy:cold"
37
+ cap_execute "deploy:migrations"
38
+ end
39
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,39 @@
1
+ require 'capistrano/cli'
2
+ require 'capistrano/configuration'
3
+
4
+ # load File.expand_path(File.dirname(__FILE__) + "/../recipes/fast_git_deploy.rb")
5
+
6
+ # load File.dirname(__FILE__) + '/config/deploy.rb'
7
+
8
+ Spec::Runner.configure do |config|
9
+ config.before :each do
10
+ FileUtils.rm_rf(File.dirname(__FILE__) + "/deployments")
11
+ end
12
+
13
+ config.after :each do
14
+ FileUtils.rm_rf(File.dirname(__FILE__) + "/deployments")
15
+ end
16
+ end
17
+
18
+ module Capistrano
19
+ class Configuration
20
+ module Actions
21
+ module Invocation
22
+ def sudo(*parameters, &block)
23
+ options = parameters.last.is_a?(Hash) ? parameters.pop.dup : {}
24
+ command = parameters.first
25
+ user = options[:as] && "-u #{options.delete(:as)}"
26
+
27
+ sudo_prompt_option = "-p '#{sudo_prompt}'" unless sudo_prompt.empty?
28
+ sudo_command = [fetch(:sudo, "sudo"), sudo_prompt_option, user].compact.join(" ")
29
+
30
+ if command
31
+ run(command, options, &block)
32
+ else
33
+ return sudo_command
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fast_git_deploy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Scott Taylor
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-08 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: The Fast Git Deploy Method - just a git reset --hard to update code
15
+ email:
16
+ - scott@railsnewbie.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - Gemfile
23
+ - Gemfile.lock
24
+ - LICENSE.txt
25
+ - README.rdoc
26
+ - fast_git_deploy.gemspec
27
+ - lib/fast_git_deploy.rb
28
+ - lib/fast_git_deploy/recipes/fast_git_deploy.rb
29
+ - lib/fast_git_deploy/recipes/fast_git_deploy/rollback.rb
30
+ - lib/fast_git_deploy/recipes/fast_git_deploy/setup.rb
31
+ - lib/fast_git_deploy/version.rb
32
+ - spec/Capfile
33
+ - spec/config/deploy.rb
34
+ - spec/deploy_spec.rb
35
+ - spec/spec.opts
36
+ - spec/spec_helper.rb
37
+ homepage: http://github.com/smtlaissezfaire/fast_git_deploy
38
+ licenses: []
39
+ post_install_message:
40
+ rdoc_options: []
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ requirements: []
56
+ rubyforge_project:
57
+ rubygems_version: 1.8.24
58
+ signing_key:
59
+ specification_version: 3
60
+ summary: Amazingly fast git deploys by using only a current directory (using git as
61
+ the version the control history). It's the same technique github uses at their
62
+ company.
63
+ test_files: []