Class: Capistrano::Deploy::SCM::Git

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

### Methods

checkout checkout command command diff diff export export handle_data handle_data head head log log origin origin query_revision query_revision sync sync

### Public Instance methods

checkout(revision, destination)

Performs a clone on the remote machine, then checkout on the branch you want to deploy.

[Source]

         # 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} sync"
    155:             execute << "#{git} submodule #{verbose} update"
    156:           end
    157:
    158:           execute.join(" && ")
    159:         end

checkout(revision, destination)

Performs a clone on the remote machine, then checkout on the branch you want to deploy.

[Source]

         # 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} sync"
    155:             execute << "#{git} submodule #{verbose} update"
    156:           end
    157:
    158:           execute.join(" && ")
    159:         end

command()

[Source]

         # File lib/capistrano/recipes/deploy/scm/git.rb, line 235
    235:         def command
    236:           # For backwards compatibility with 1.x version of this module
    237:           configuration[:git] || super
    238:         end

command()

[Source]

         # File lib/capistrano/recipes/deploy/scm/git.rb, line 235
    235:         def command
    236:           # For backwards compatibility with 1.x version of this module
    237:           configuration[:git] || super
    238:         end

diff(from, to=nil)

Returns a string of diffs between two revisions

[Source]

         # File lib/capistrano/recipes/deploy/scm/git.rb, line 205
    205:         def diff(from, to=nil)
    206:           from << "..#{to}" if to
    207:           scm :diff, from
    208:         end

diff(from, to=nil)

Returns a string of diffs between two revisions

[Source]

         # File lib/capistrano/recipes/deploy/scm/git.rb, line 205
    205:         def diff(from, to=nil)
    206:           from << "..#{to}" if to
    207:           scm :diff, from
    208:         end

export(revision, destination)

An expensive export. Performs a checkout as above, then removes the repo.

[Source]

         # File lib/capistrano/recipes/deploy/scm/git.rb, line 163
    163:         def export(revision, destination)
    164:           checkout(revision, destination) << " && rm -Rf #{destination}/.git"
    165:         end

export(revision, destination)

An expensive export. Performs a checkout as above, then removes the repo.

[Source]

         # File lib/capistrano/recipes/deploy/scm/git.rb, line 163
    163:         def export(revision, destination)
    164:           checkout(revision, destination) << " && rm -Rf #{destination}/.git"
    165:         end

handle_data(state, stream, text)

Determines what the response should be for a particular bit of text from the SCM. Password prompts, connection requests, passphrases, etc. are handled here.

[Source]

         # File lib/capistrano/recipes/deploy/scm/git.rb, line 243
    243:         def handle_data(state, stream, text)
    244:           host = state[:channel][:host]
    245:           logger.info "[#{host} :: #{stream}] #{text}"
    246:           case text
    247:           when /\bpassword.*:/i
    248:             # git is prompting for a password
    249:             unless pass = configuration[:scm_password]
    250:               pass = Capistrano::CLI.password_prompt
    251:             end
    252:             "#{pass}\n"
    253:           when %r{\(yes/no\)}
    254:             # git is asking whether or not to connect
    255:             "yes\n"
    256:           when /passphrase/i
    257:             # git is asking for the passphrase for the user's key
    258:             unless pass = configuration[:scm_passphrase]
    259:               pass = Capistrano::CLI.password_prompt
    260:             end
    261:             "#{pass}\n"
    262:           when /accept \(t\)emporarily/
    263:             # git is asking whether to accept the certificate
    264:             "t\n"
    265:           end
    266:         end

handle_data(state, stream, text)

Determines what the response should be for a particular bit of text from the SCM. Password prompts, connection requests, passphrases, etc. are handled here.

[Source]

         # File lib/capistrano/recipes/deploy/scm/git.rb, line 243
    243:         def handle_data(state, stream, text)
    244:           host = state[:channel][:host]
    245:           logger.info "[#{host} :: #{stream}] #{text}"
    246:           case text
    247:           when /\bpassword.*:/i
    248:             # git is prompting for a password
    249:             unless pass = configuration[:scm_password]
    250:               pass = Capistrano::CLI.password_prompt
    251:             end
    252:             "#{pass}\n"
    253:           when %r{\(yes/no\)}
    254:             # git is asking whether or not to connect
    255:             "yes\n"
    256:           when /passphrase/i
    257:             # git is asking for the passphrase for the user's key
    258:             unless pass = configuration[:scm_passphrase]
    259:               pass = Capistrano::CLI.password_prompt
    260:             end
    261:             "#{pass}\n"
    262:           when /accept \(t\)emporarily/
    263:             # git is asking whether to accept the certificate
    264:             "t\n"
    265:           end
    266:         end

head()

When referencing "head", use the branch we want to deploy or, by default, Git‘s reference of HEAD (the latest changeset in the default branch, usually called "master").

[Source]

         # File lib/capistrano/recipes/deploy/scm/git.rb, line 122
    122:         def head
    123:           configuration[:branch] || 'HEAD'
    124:         end

head()

When referencing "head", use the branch we want to deploy or, by default, Git‘s reference of HEAD (the latest changeset in the default branch, usually called "master").

[Source]

         # File lib/capistrano/recipes/deploy/scm/git.rb, line 122
    122:         def head
    123:           configuration[:branch] || 'HEAD'
    124:         end

log(from, to=nil)

Returns a log of changes between the two revisions (inclusive).

[Source]

         # File lib/capistrano/recipes/deploy/scm/git.rb, line 211
    211:         def log(from, to=nil)
    212:           scm :log, "#{from}..#{to}"
    213:         end

log(from, to=nil)

Returns a log of changes between the two revisions (inclusive).

[Source]

         # File lib/capistrano/recipes/deploy/scm/git.rb, line 211
    211:         def log(from, to=nil)
    212:           scm :log, "#{from}..#{to}"
    213:         end

origin()

[Source]

         # File lib/capistrano/recipes/deploy/scm/git.rb, line 126
    126:         def origin
    127:           configuration[:remote] || 'origin'
    128:         end

origin()

[Source]

         # File lib/capistrano/recipes/deploy/scm/git.rb, line 126
    126:         def origin
    127:           configuration[:remote] || 'origin'
    128:         end

query_revision(revision) {|command| ...}

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

[Source]

         # File lib/capistrano/recipes/deploy/scm/git.rb, line 217
    217:         def query_revision(revision)
    218:           raise ArgumentError, "Deploying remote branches is no longer supported.  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\//
    219:           return revision if revision =~ /^[0-9a-f]{40}$/
    220:           command = scm('ls-remote', repository, revision)
    221:           result = yield(command)
    222:           revdata = result.split(/[\t\n]/)
    223:           newrev = nil
    224:           revdata.each_slice(2) do |refs|
    225:             rev, ref = *refs
    226:             if ref.sub(/refs\/.*?\//, '').strip == revision.to_s
    227:               newrev = rev
    228:               break
    229:             end
    230:           end
    231:           raise "Unable to resolve revision for '#{revision}' on repository '#{repository}'." unless newrev =~ /^[0-9a-f]{40}$/
    232:           return newrev
    233:         end

query_revision(revision) {|command| ...}

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

[Source]

         # File lib/capistrano/recipes/deploy/scm/git.rb, line 217
    217:         def query_revision(revision)
    218:           raise ArgumentError, "Deploying remote branches is no longer supported.  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\//
    219:           return revision if revision =~ /^[0-9a-f]{40}$/
    220:           command = scm('ls-remote', repository, revision)
    221:           result = yield(command)
    222:           revdata = result.split(/[\t\n]/)
    223:           newrev = nil
    224:           revdata.each_slice(2) do |refs|
    225:             rev, ref = *refs
    226:             if ref.sub(/refs\/.*?\//, '').strip == revision.to_s
    227:               newrev = rev
    228:               break
    229:             end
    230:           end
    231:           raise "Unable to resolve revision for '#{revision}' on repository '#{repository}'." unless newrev =~ /^[0-9a-f]{40}$/
    232:           return newrev
    233:         end

sync(revision, destination)

Merges the changes to ‘head’ since the last fetch, for remote_cache deployment strategy

[Source]

         # File lib/capistrano/recipes/deploy/scm/git.rb, line 169
    169:         def sync(revision, destination)
    170:           git     = command
    171:           remote  = origin
    172:
    173:           execute = []
    174:           execute << "cd #{destination}"
    175:
    176:           # Use git-config to setup a remote tracking branches. Could use
    177:           # git-remote but it complains when a remote of the same name already
    178:           # exists, git-config will just silenty overwrite the setting every
    179:           # time. This could cause wierd-ness in the remote cache if the url
    180:           # changes between calls, but as long as the repositories are all
    181:           # based from each other it should still work fine.
    182:           if remote != 'origin'
    183:             execute << "#{git} config remote.#{remote}.url #{configuration[:repository]}"
    184:             execute << "#{git} config remote.#{remote}.fetch +refs/heads/*:refs/remotes/#{remote}/*"
    185:           end
    186:
    187:           # since we're in a local branch already, just reset to specified revision rather than merge
    188:           execute << "#{git} fetch #{verbose} #{remote} && #{git} reset #{verbose} --hard #{revision}"
    189:
    190:           if configuration[:git_enable_submodules]
    191:             execute << "#{git} submodule #{verbose} init"
    192:             execute << "for mod in `#{git} submodule status | awk '{ print $2 }'`; do #{git} config -f .git/config submodule.${mod}.url `#{git} config -f .gitmodules --get submodule.${mod}.url` && echo Synced $mod; done"
    193:             execute << "#{git} submodule #{verbose} sync"
    194:             execute << "#{git} submodule #{verbose} update"
    195:           end
    196:
    197:           # Make sure there's nothing else lying around in the repository (for
    198:           # example, a submodule that has subsequently been removed).
    199:           execute << "#{git} clean #{verbose} -d -x -f"
    200:
    201:           execute.join(" && ")
    202:         end

sync(revision, destination)

Merges the changes to ‘head’ since the last fetch, for remote_cache deployment strategy

[Source]

         # File lib/capistrano/recipes/deploy/scm/git.rb, line 169
    169:         def sync(revision, destination)
    170:           git     = command
    171:           remote  = origin
    172:
    173:           execute = []
    174:           execute << "cd #{destination}"
    175:
    176:           # Use git-config to setup a remote tracking branches. Could use
    177:           # git-remote but it complains when a remote of the same name already
    178:           # exists, git-config will just silenty overwrite the setting every
    179:           # time. This could cause wierd-ness in the remote cache if the url
    180:           # changes between calls, but as long as the repositories are all
    181:           # based from each other it should still work fine.
    182:           if remote != 'origin'
    183:             execute << "#{git} config remote.#{remote}.url #{configuration[:repository]}"
    184:             execute << "#{git} config remote.#{remote}.fetch +refs/heads/*:refs/remotes/#{remote}/*"
    185:           end
    186:
    187:           # since we're in a local branch already, just reset to specified revision rather than merge
    188:           execute << "#{git} fetch #{verbose} #{remote} && #{git} reset #{verbose} --hard #{revision}"
    189:
    190:           if configuration[:git_enable_submodules]
    191:             execute << "#{git} submodule #{verbose} init"
    192:             execute << "for mod in `#{git} submodule status | awk '{ print $2 }'`; do #{git} config -f .git/config submodule.${mod}.url `#{git} config -f .gitmodules --get submodule.${mod}.url` && echo Synced $mod; done"
    193:             execute << "#{git} submodule #{verbose} sync"
    194:             execute << "#{git} submodule #{verbose} update"
    195:           end
    196:
    197:           # Make sure there's nothing else lying around in the repository (for
    198:           # example, a submodule that has subsequently been removed).
    199:           execute << "#{git} clean #{verbose} -d -x -f"
    200:
    201:           execute.join(" && ")
    202:         end

[Validate]