git-deploy 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2009 Mislav Marohnić
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/lib/git_deploy.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'capistrano/recipes/deploy/scm/git'
2
- require 'capistrano/recipes/deploy/strategy/checkout'
3
2
 
4
3
  Capistrano::Configuration.instance(true).load do
5
4
  def _cset(name, *args, &block)
@@ -15,23 +14,13 @@ Capistrano::Configuration.instance(true).load do
15
14
  _cset(:multiple_hosts) { roles.values.map{ |v| v.servers}.flatten.uniq.size > 1 }
16
15
  _cset(:repository) { `#{ source.local.scm('config', "remote.#{remote}.url") }`.chomp }
17
16
  _cset(:remote_host) { repository.split(':', 2).first }
18
- _cset(:deploy_to) { repository.split(':', 2).last } # "/u/apps/#{application}"
17
+ _cset(:deploy_to) { repository.split(':', 2).last }
19
18
  _cset(:run_method) { fetch(:use_sudo, true) ? :sudo : :run }
20
19
  _cset :group_writeable, false
21
20
 
22
21
  _cset(:current_branch) { File.read('.git/HEAD').chomp.split(' refs/heads/').last }
23
22
  _cset(:revision) { branch }
24
23
  _cset(:source) { Capistrano::Deploy::SCM::Git.new(self) }
25
- _cset(:strategy) { Capistrano::Deploy::Strategy::Checkout.new(self) }
26
-
27
- # Helper for the `deploy:check' task
28
- def depend(location, type, *args)
29
- deps = fetch(:dependencies, {})
30
- deps[location] ||= {}
31
- deps[location][type] ||= []
32
- deps[location][type] << args
33
- set :dependencies, deps
34
- end
35
24
 
36
25
  # If :run_method is :sudo (or :use_sudo is true), this executes the given command
37
26
  # via +sudo+. Otherwise is uses +run+. If :as is given as a key, it will be
@@ -44,10 +33,10 @@ Capistrano::Configuration.instance(true).load do
44
33
  raise ArgumentError, "too many arguments" if args.any?
45
34
 
46
35
  as = options.fetch(:as, fetch(:admin_runner, nil))
47
- via = fetch(:run_method, :sudo)
36
+
48
37
  if command
49
- invoke_command(command, :via => via, :as => as)
50
- elsif via == :sudo
38
+ invoke_command(command, :via => run_method, :as => as)
39
+ elsif :sudo == run_method
51
40
  sudo(:as => as)
52
41
  else
53
42
  ""
@@ -57,25 +46,43 @@ Capistrano::Configuration.instance(true).load do
57
46
  namespace :deploy do
58
47
  desc "Deploys your project."
59
48
  task :default do
60
- push
49
+ unless multiple_hosts
50
+ push
51
+ else
52
+ code
53
+ command = ["cd #{deploy_to}"]
54
+ command << ".git/hooks/post-reset `cat .git/ORIG_HEAD` HEAD 2>&1 | tee -a log/deploy.log"
55
+
56
+ run command.join(' && ')
57
+ end
61
58
  end
62
59
 
63
60
  task :push do
64
61
  system source.local.scm('push', remote, "#{revision}:#{branch}")
65
62
  end
66
63
 
64
+ task :code do
65
+ command = ["cd #{deploy_to}"]
66
+ command << source.scm('fetch', remote, "+refs/heads/#{branch}:refs/remotes/origin/#{branch}")
67
+ command << source.scm('reset', '--hard', "origin/#{branch}")
68
+
69
+ run command.join(' && ')
70
+ end
71
+
67
72
  desc "Prepares servers for deployment."
68
73
  task :setup do
74
+ shared = fetch(:group_writeable)
75
+
69
76
  command = ["#{try_sudo} mkdir -p #{deploy_to}"]
70
- command << "#{try_sudo} chown $USER #{deploy_to}" if fetch(:run_method, :sudo) == :sudo
77
+ command << "#{try_sudo} chown $USER #{deploy_to}" if :sudo == run_method
71
78
  command << "cd #{deploy_to}"
72
- command << "chmod g+w ."
73
- command << "git init #{fetch(:group_writeable) ? '--shared' : ''}"
79
+ command << "chmod g+w ." if shared
80
+ command << "git init #{shared ? '--shared' : ''}"
74
81
  command << "sed -i'' -e 's/master/#{branch}/' .git/HEAD" unless branch == 'master'
75
- command << "git config --bool receive.denyNonFastForwards false" if fetch(:group_writeable)
82
+ command << "git config --bool receive.denyNonFastForwards false" if shared
76
83
  command << "git config receive.denyCurrentBranch ignore"
77
84
  run command.join(' && ')
78
-
85
+
79
86
  install_hooks
80
87
  push
81
88
  end
@@ -95,39 +102,20 @@ Capistrano::Configuration.instance(true).load do
95
102
  end
96
103
 
97
104
  desc <<-DESC
98
- Test deployment dependencies.
105
+ Copy files to the currently deployed version. Use a comma-separated \
106
+ list in FILES to specify which files to upload.
99
107
 
100
- You can define your own dependencies, as well, using the `depend' method:
108
+ Note that unversioned files on your server are likely to be \
109
+ overwritten by the next push. Always persist your changes by committing.
101
110
 
102
- depend :remote, :gem, "tzinfo", ">=0.3.3"
103
- depend :local, :command, "svn"
104
- depend :remote, :directory, "/u/depot/files"
111
+ $ cap deploy:upload FILES=templates,controller.rb
112
+ $ cap deploy:upload FILES='config/apache/*.conf'
105
113
  DESC
106
- task :check do
107
- dependencies = strategy.check!
108
-
109
- other = fetch(:dependencies, {})
110
- other.each do |location, types|
111
- types.each do |type, calls|
112
- if type == :gem
113
- dependencies.send(location).command(fetch(:gem_command, "gem")).or("`gem' command could not be found. Try setting :gem_command")
114
- end
115
-
116
- calls.each do |args|
117
- dependencies.send(location).send(type, *args)
118
- end
119
- end
120
- end
114
+ task :upload do
115
+ files = (ENV["FILES"] || "").split(",").map { |f| Dir[f.strip] }.flatten
116
+ abort "Please specify at least one file or directory to update (via the FILES environment variable)" if files.empty?
121
117
 
122
- if dependencies.pass?
123
- puts "You appear to have all necessary dependencies installed"
124
- else
125
- puts "The following dependencies failed. Please check them and try again:"
126
- dependencies.reject { |d| d.pass? }.each do |d|
127
- puts "--> #{d.message}"
128
- end
129
- abort
130
- end
118
+ files.each { |file| top.upload(file, File.join(deploy_to, file)) }
131
119
  end
132
120
  end
133
121
  end
@@ -1,4 +1,4 @@
1
- #!/usr/bin/ruby
1
+ #!/usr/bin/env ruby
2
2
  if ENV['GIT_DIR'] == '.'
3
3
  # this means the script has been called as a hook, not manually.
4
4
  # get the proper GIT_DIR so we can descend into the working copy dir;
@@ -1,8 +1,40 @@
1
- #!/usr/bin/ruby
1
+ #!/usr/bin/env ruby
2
2
  RAILS_ENV = 'production'
3
3
  oldrev, newrev = ARGV
4
4
  $stdout.sync = true
5
5
 
6
+ def parse_configuration(file)
7
+ config = {}
8
+ current = nil
9
+
10
+ File.open(file).each_line do |line|
11
+ case line
12
+ when /^\[(\w+)(?: "(.+)")\]/
13
+ key, subkey = $1, $2
14
+ current = (config[key] ||= {})
15
+ current = (current[subkey] ||= {}) if subkey
16
+ else
17
+ key, value = line.strip.split(' = ')
18
+ current[key] = value
19
+ end
20
+ end
21
+
22
+ config
23
+ end
24
+
25
+ class Array
26
+ # scans the list of files to see if any of them are under the given path
27
+ def any_in_dir?(dir)
28
+ if Array === dir
29
+ exp = %r{^(?:#{dir.join('|')})/}
30
+ any? { |file| file =~ exp }
31
+ else
32
+ dir += '/'
33
+ any? { |file| file.index(dir) == 0 }
34
+ end
35
+ end
36
+ end
37
+
6
38
  # get a list of files that changed
7
39
  changes = `git diff #{oldrev} #{newrev} --diff-filter=ACDMR --name-status`.split("\n")
8
40
 
@@ -20,19 +52,6 @@ deleted_files = changes_hash['D'] # deleted
20
52
  changed_files = modified_files + deleted_files # all
21
53
  puts "files changed: #{changed_files.size}"
22
54
 
23
- class Array
24
- # scans the list of files to see if any of them are under the given path
25
- def any_in_dir?(dir)
26
- if Array === dir
27
- exp = %r{^(?:#{dir.join('|')})/}
28
- any? { |file| file =~ exp }
29
- else
30
- dir += '/'
31
- any? { |file| file.index(dir) == 0 }
32
- end
33
- end
34
- end
35
-
36
55
  cached_assets_cleared = false
37
56
 
38
57
  # detect modified asset dirs
@@ -43,11 +62,8 @@ end
43
62
 
44
63
  unless asset_dirs.empty?
45
64
  # clear cached assets (unversioned/ignored files)
46
- deleted_assets = `git ls-files -z --other -- #{asset_dirs.join(' ')} | xargs -0 rm -v`.split("\n")
47
- unless deleted_assets.empty?
48
- puts "cleared: #{deleted_assets.join(', ')}"
49
- cached_assets_cleared = true
50
- end
65
+ system %(git clean -x -f -- #{asset_dirs.join(' ')})
66
+ cached_assets_cleared = true
51
67
  end
52
68
 
53
69
  # run migrations when new ones added
@@ -56,14 +72,35 @@ if new_migrations = added_files.any_in_dir?('db/migrate')
56
72
  end
57
73
 
58
74
  if modified_files.include?('.gitmodules')
59
- # sync submodule remote urls in case of changes
60
- system %(git submodule sync)
61
75
  # initialize new submodules
62
76
  system %(git submodule init)
77
+ # sync submodule remote urls in case of changes
78
+ config = parse_configuration('.gitmodules')
79
+
80
+ if config['submodule']
81
+ config['submodule'].values.each do |submodule|
82
+ path = submodule['path']
83
+ subconf = "#{path}/.git/config"
84
+
85
+ if File.exists? subconf
86
+ old_url = `git config -f "#{subconf}" remote.origin.url`.chomp
87
+ new_url = submodule['url']
88
+ unless old_url == new_url
89
+ puts "changing #{path.inspect} URL:\n #{old_url.inspect} → #{new_url.inspect}"
90
+ `git config -f "#{subconf}" remote.origin.url "#{new_url}"`
91
+ end
92
+ else
93
+ $stderr.puts "a submodule in #{path.inspect} doesn't exist"
94
+ end
95
+ end
96
+ end
63
97
  end
64
98
  # update existing submodules
65
99
  system %(git submodule update)
66
100
 
101
+ # clean unversioned files from vendor (e.g. old submodules)
102
+ system %(git clean -d -f vendor)
103
+
67
104
  # determine if app restart is needed
68
105
  if cached_assets_cleared or new_migrations or changed_files.any_in_dir?(%w(app config lib public vendor))
69
106
  require 'fileutils'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-deploy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - "Mislav Marohni\xC4\x87"
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-04 00:00:00 +02:00
12
+ date: 2009-11-25 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -22,7 +22,7 @@ dependencies:
22
22
  - !ruby/object:Gem::Version
23
23
  version: 2.5.9
24
24
  version:
25
- description: git-deploy is a tool to install useful git hooks on your remote repository to enable git push-based, Heroku-like deployment on your host.
25
+ description: A tool to install useful git hooks on your remote repository to enable push-based, Heroku-like deployment on your host.
26
26
  email: mislav.marohnic@gmail.com
27
27
  executables: []
28
28
 
@@ -31,12 +31,12 @@ extensions: []
31
31
  extra_rdoc_files: []
32
32
 
33
33
  files:
34
- - Rakefile
35
34
  - lib/git_deploy.rb
36
35
  - lib/hooks/post-receive.rb
37
36
  - lib/hooks/post-reset.rb
38
37
  - README.markdown
39
- has_rdoc: true
38
+ - LICENSE
39
+ has_rdoc: false
40
40
  homepage: http://github.com/mislav/git-deploy
41
41
  licenses: []
42
42
 
data/Rakefile DELETED
@@ -1,34 +0,0 @@
1
- desc "generates .gemspec file"
2
- task :gemspec do
3
- spec = Gem::Specification.new do |p|
4
- p.name = 'git-deploy'
5
- p.version = '0.2.0'
6
-
7
- p.summary = "Simple git push-based application deployment"
8
- p.description = "git-deploy is a tool to install useful git hooks on your remote repository to enable git push-based, Heroku-like deployment on your host."
9
-
10
- p.author = 'Mislav Marohnić'
11
- p.email = 'mislav.marohnic@gmail.com'
12
- p.homepage = 'http://github.com/mislav/git-deploy'
13
-
14
- p.add_dependency 'capistrano', '~> 2.5.9'
15
-
16
- p.files = FileList.new('Rakefile', '{bin,lib,sample,test,spec,rails}/**/*', 'README*', 'LICENSE*', 'CHANGELOG*')
17
- p.files &= `git ls-files -z`.split("\0")
18
-
19
- p.executables = Dir['bin/*'].map { |f| File.basename(f) }
20
-
21
- p.rubyforge_project = nil
22
- p.has_rdoc = false
23
- end
24
-
25
- spec_string = spec.to_ruby
26
-
27
- begin
28
- Thread.new { eval("$SAFE = 3\n#{spec_string}", binding) }.join
29
- rescue
30
- abort "unsafe gemspec: #{$!}"
31
- else
32
- File.open("#{spec.name}.gemspec", 'w') { |file| file.write spec_string }
33
- end
34
- end