| Class | Capistrano::Deploy::SCM::Git |
| In: |
lib/capistrano/recipes/deploy/scm/git.rb
lib/capistrano/recipes/deploy/scm/git.rb |
| Parent: | Base |
An SCM module for using Git as your source control tool with Capistrano 2.0. If you are using Capistrano 1.x, use this plugin instead:
http://scie.nti.st/2007/3/16/capistrano-with-git-shared-repository
Assumes you are using a shared Git repository.
Parts of this plugin borrowed from Scott Chacon‘s version, which I found on the Capistrano mailing list but failed to be able to get working.
FEATURES:
* Very simple, only requiring 2 lines in your deploy.rb.
* Can deploy different branches, tags, or any SHA1 easily.
* Supports prompting for password / passphrase upon checkout.
(I am amazed at how some plugins don't do this)
* Supports :scm_command, :scm_password, :scm_passphrase Capistrano
directives.
CONFIGURATION
Use this plugin by adding the following line in your config/deploy.rb:
set :scm, :git
Set :repository to the path of your Git repo:
set :repository, "someuser@somehost:/home/myproject"
The above two options are required to be set, the ones below are optional.
You may set :branch, which is the reference to the branch, tag, or any SHA1 you are deploying, for example:
set :branch, "master"
Otherwise, HEAD is assumed. I strongly suggest you set this. HEAD is not always the best assumption.
You may also set :remote, which will be used as a name for remote tracking of repositories. This option is intended for use with the :remote_cache strategy in a distributed git environment.
For example in the projects config/deploy.rb:
set :repository, "#{scm_user}@somehost:~/projects/project.git"
set :remote, "#{scm_user}"
Then each person with deploy priveledges can add the following to their local ~/.caprc file:
set :scm_user, 'someuser'
Now any time a person deploys the project, their repository will be setup as a remote git repository within the cached repository.
The :scm_command configuration variable, if specified, will be used as the full path to the git executable on the remote machine:
set :scm_command, "/opt/local/bin/git"
For compatibility with deploy scripts that may have used the 1.x version of this plugin before upgrading, :git is still recognized as an alias for :scm_command.
Set :scm_password to the password needed to clone your repo if you don‘t have password-less (public key) entry:
set :scm_password, "my_secret'
Otherwise, you will be prompted for a password.
:scm_passphrase is also supported.
The remote cache strategy is also supported.
set :repository_cache, "git_master" set :deploy_via, :remote_cache
For faster clone, you can also use shallow cloning. This will set the ’—depth’ flag using the depth specified. This cannot be used together with the :remote_cache strategy
set :git_shallow_clone, 1
For those that don‘t like to leave your entire repository on your production server you can:
set :deploy_via, :export
To deploy from a local repository:
set :repository, "file://." set :deploy_via, :copy
AUTHORS
Garry Dolley scie.nti.st Contributions by Geoffrey Grosenbach topfunky.com
Scott Chacon http://jointheconversation.org
Alex Arnell http://twologic.com
and Phillip Goldenburg
Performs a clone on the remote machine, then checkout on the branch you want to deploy.
# File lib/capistrano/recipes/deploy/scm/git.rb, line 132
132: def checkout(revision, destination)
133: git = command
134: remote = origin
135:
136: args = []
137: args << "-o #{remote}" unless remote == 'origin'
138: if depth = configuration[:git_shallow_clone]
139: args << "--depth #{depth}"
140: end
141:
142: execute = []
143: if args.empty?
144: execute << "#{git} clone #{verbose} #{configuration[:repository]} #{destination}"
145: else
146: execute << "#{git} clone #{verbose} #{args.join(' ')} #{configuration[:repository]} #{destination}"
147: end
148:
149: # checkout into a local branch rather than a detached HEAD
150: execute << "cd #{destination} && #{git} checkout #{verbose} -b deploy #{revision}"
151:
152: if configuration[:git_enable_submodules]
153: execute << "#{git} submodule #{verbose} init"
154: execute << "#{git} submodule #{verbose} update"
155: end
156:
157: execute.join(" && ")
158: end
Performs a clone on the remote machine, then checkout on the branch you want to deploy.
# File lib/capistrano/recipes/deploy/scm/git.rb, line 132
132: def checkout(revision, destination)
133: git = command
134: remote = origin
135:
136: args = []
137: args << "-o #{remote}" unless remote == 'origin'
138: if depth = configuration[:git_shallow_clone]
139: args << "--depth #{depth}"
140: end
141:
142: execute = []
143: if args.empty?
144: execute << "#{git} clone #{verbose} #{configuration[:repository]} #{destination}"
145: else
146: execute << "#{git} clone #{verbose} #{args.join(' ')} #{configuration[:repository]} #{destination}"
147: end
148:
149: # checkout into a local branch rather than a detached HEAD
150: execute << "cd #{destination} && #{git} checkout #{verbose} -b deploy #{revision}"
151:
152: if configuration[:git_enable_submodules]
153: execute << "#{git} submodule #{verbose} init"
154: execute << "#{git} submodule #{verbose} update"
155: end
156:
157: execute.join(" && ")
158: end
# File lib/capistrano/recipes/deploy/scm/git.rb, line 228
228: def command
229: # For backwards compatibility with 1.x version of this module
230: configuration[:git] || super
231: end
# File lib/capistrano/recipes/deploy/scm/git.rb, line 228
228: def command
229: # For backwards compatibility with 1.x version of this module
230: configuration[:git] || super
231: end
Returns a string of diffs between two revisions
# File lib/capistrano/recipes/deploy/scm/git.rb, line 198
198: def diff(from, to=nil)
199: from << "..#{to}" if to
200: scm :diff, from
201: end
Returns a string of diffs between two revisions
# File lib/capistrano/recipes/deploy/scm/git.rb, line 198
198: def diff(from, to=nil)
199: from << "..#{to}" if to
200: scm :diff, from
201: end
Determines what the response should be for a particular bit of text from the SCM. Password prompts, connection requests, passphrases, etc. are handled here.
# File lib/capistrano/recipes/deploy/scm/git.rb, line 236
236: def handle_data(state, stream, text)
237: host = state[:channel][:host]
238: logger.info "[#{host} :: #{stream}] #{text}"
239: case text
240: when /\bpassword.*:/i
241: # git is prompting for a password
242: unless pass = configuration[:scm_password]
243: pass = Capistrano::CLI.password_prompt
244: end
245: "#{pass}\n"
246: when %r{\(yes/no\)}
247: # git is asking whether or not to connect
248: "yes\n"
249: when /passphrase/i
250: # git is asking for the passphrase for the user's key
251: unless pass = configuration[:scm_passphrase]
252: pass = Capistrano::CLI.password_prompt
253: end
254: "#{pass}\n"
255: when /accept \(t\)emporarily/
256: # git is asking whether to accept the certificate
257: "t\n"
258: end
259: end
Determines what the response should be for a particular bit of text from the SCM. Password prompts, connection requests, passphrases, etc. are handled here.
# File lib/capistrano/recipes/deploy/scm/git.rb, line 236
236: def handle_data(state, stream, text)
237: host = state[:channel][:host]
238: logger.info "[#{host} :: #{stream}] #{text}"
239: case text
240: when /\bpassword.*:/i
241: # git is prompting for a password
242: unless pass = configuration[:scm_password]
243: pass = Capistrano::CLI.password_prompt
244: end
245: "#{pass}\n"
246: when %r{\(yes/no\)}
247: # git is asking whether or not to connect
248: "yes\n"
249: when /passphrase/i
250: # git is asking for the passphrase for the user's key
251: unless pass = configuration[:scm_passphrase]
252: pass = Capistrano::CLI.password_prompt
253: end
254: "#{pass}\n"
255: when /accept \(t\)emporarily/
256: # git is asking whether to accept the certificate
257: "t\n"
258: end
259: end
# File lib/capistrano/recipes/deploy/scm/git.rb, line 126
126: def origin
127: configuration[:remote] || 'origin'
128: end
# File lib/capistrano/recipes/deploy/scm/git.rb, line 126
126: def origin
127: configuration[:remote] || 'origin'
128: end
Getting the actual commit id, in case we were passed a tag or partial sha or something - it will return the sha if you pass a sha, too
# File lib/capistrano/recipes/deploy/scm/git.rb, line 210
210: def query_revision(revision)
211: raise ArgumentError, "Deploying remote branches has been deprecated. Specify the remote branch as a local branch for the git repository you're deploying from (ie: '#{revision.gsub('origin/', '')}' rather than '#{revision}')." if revision =~ /^origin\//
212: return revision if revision =~ /^[0-9a-f]{40}$/
213: command = scm('ls-remote', repository, revision)
214: result = yield(command)
215: revdata = result.split(/[\t\n]/)
216: newrev = nil
217: revdata.each_slice(2) do |refs|
218: rev, ref = *refs
219: if ref.sub(/refs\/.*?\//, '').strip == revision
220: newrev = rev
221: break
222: end
223: end
224: raise "Unable to resolve revision for '#{revision}' on repository '#{repository}'." unless newrev =~ /^[0-9a-f]{40}$/
225: return newrev
226: end
Getting the actual commit id, in case we were passed a tag or partial sha or something - it will return the sha if you pass a sha, too
# File lib/capistrano/recipes/deploy/scm/git.rb, line 210
210: def query_revision(revision)
211: raise ArgumentError, "Deploying remote branches has been deprecated. Specify the remote branch as a local branch for the git repository you're deploying from (ie: '#{revision.gsub('origin/', '')}' rather than '#{revision}')." if revision =~ /^origin\//
212: return revision if revision =~ /^[0-9a-f]{40}$/
213: command = scm('ls-remote', repository, revision)
214: result = yield(command)
215: revdata = result.split(/[\t\n]/)
216: newrev = nil
217: revdata.each_slice(2) do |refs|
218: rev, ref = *refs
219: if ref.sub(/refs\/.*?\//, '').strip == revision
220: newrev = rev
221: break
222: end
223: end
224: raise "Unable to resolve revision for '#{revision}' on repository '#{repository}'." unless newrev =~ /^[0-9a-f]{40}$/
225: return newrev
226: end
Merges the changes to ‘head’ since the last fetch, for remote_cache deployment strategy
# File lib/capistrano/recipes/deploy/scm/git.rb, line 168
168: def sync(revision, destination)
169: git = command
170: remote = origin
171:
172: execute = []
173: execute << "cd #{destination}"
174:
175: # Use git-config to setup a remote tracking branches. Could use
176: # git-remote but it complains when a remote of the same name already
177: # exists, git-config will just silenty overwrite the setting every
178: # time. This could cause wierd-ness in the remote cache if the url
179: # changes between calls, but as long as the repositories are all
180: # based from each other it should still work fine.
181: if remote != 'origin'
182: execute << "#{git} config remote.#{remote}.url #{configuration[:repository]}"
183: execute << "#{git} config remote.#{remote}.fetch +refs/heads/*:refs/remotes/#{remote}/*"
184: end
185:
186: # since we're in a local branch already, just reset to specified revision rather than merge
187: execute << "#{git} fetch #{verbose} #{remote} && #{git} reset #{verbose} --hard #{revision}"
188:
189: if configuration[:git_enable_submodules]
190: execute << "#{git} submodule #{verbose} init"
191: execute << "#{git} submodule #{verbose} update"
192: end
193:
194: execute.join(" && ")
195: end
Merges the changes to ‘head’ since the last fetch, for remote_cache deployment strategy
# File lib/capistrano/recipes/deploy/scm/git.rb, line 168
168: def sync(revision, destination)
169: git = command
170: remote = origin
171:
172: execute = []
173: execute << "cd #{destination}"
174:
175: # Use git-config to setup a remote tracking branches. Could use
176: # git-remote but it complains when a remote of the same name already
177: # exists, git-config will just silenty overwrite the setting every
178: # time. This could cause wierd-ness in the remote cache if the url
179: # changes between calls, but as long as the repositories are all
180: # based from each other it should still work fine.
181: if remote != 'origin'
182: execute << "#{git} config remote.#{remote}.url #{configuration[:repository]}"
183: execute << "#{git} config remote.#{remote}.fetch +refs/heads/*:refs/remotes/#{remote}/*"
184: end
185:
186: # since we're in a local branch already, just reset to specified revision rather than merge
187: execute << "#{git} fetch #{verbose} #{remote} && #{git} reset #{verbose} --hard #{revision}"
188:
189: if configuration[:git_enable_submodules]
190: execute << "#{git} submodule #{verbose} init"
191: execute << "#{git} submodule #{verbose} update"
192: end
193:
194: execute.join(" && ")
195: end