git-deploy 0.2.0 → 0.3.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/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