switchtower 0.10.0 → 1.0.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.
@@ -53,8 +53,8 @@ module SwitchTower
53
53
  def logging_commands(directory = nil)
54
54
  log = "#{configuration.deploy_to}/revisions.log"
55
55
 
56
- "echo `date +\"%Y-%m-%d %H:%M:%S\"` $USER #{configuration.revision} #{directory} >> #{log} && " +
57
- "chmod 666 #{log};"
56
+ "(test -e #{log} || touch #{log} && chmod 666 #{log}) && " +
57
+ "echo `date +\"%Y-%m-%d %H:%M:%S\"` $USER #{configuration.revision} #{directory} >> #{log};"
58
58
  end
59
59
  end
60
60
 
@@ -0,0 +1,118 @@
1
+ require 'switchtower/scm/base'
2
+
3
+ module SwitchTower
4
+ module SCM
5
+
6
+ # An SCM module for using Bazaar as your source control tool. This
7
+ # module is used by default, but you can explicitly specify it by
8
+ # placing the following line in your configuration:
9
+ #
10
+ # set :scm, :baz
11
+ #
12
+ # Also, this module accepts a <tt>:baz</tt> configuration variable,
13
+ # which (if specified) will be used as the full path to the svn
14
+ # executable on the remote machine:
15
+ #
16
+ # set :baz, "/opt/local/bin/baz"
17
+ #
18
+ # Set the version you wish to deploy as the repository variable,
19
+ # for example:
20
+ #
21
+ # set :repository, "you@example.com--dev/yourstuff--trunk--1.0"
22
+ #
23
+ # Ensure that you have already registered the archive on the target
24
+ # machines.
25
+ #
26
+ # As bazaar keeps a great deal of extra information on a checkout,
27
+ # you will probably want to use export instead:
28
+ #
29
+ # set :checkout, "export"
30
+ #
31
+ # TODO: provide setup recipe to register archive
32
+ class Baz < Base
33
+ # Return an integer identifying the last known revision in the baz
34
+ # repository. (This integer is currently the revision number.)
35
+ def latest_revision
36
+ `#{baz} revisions #{configuration.repository}`.split.last =~ /\-(\d+)$/
37
+ $1
38
+ end
39
+
40
+ # Return the number of the revision currently deployed.
41
+ def current_revision(actor)
42
+ latest = actor.releases.last
43
+ grep = %(grep " #{latest}$" #{configuration.deploy_to}/revisions.log)
44
+ result = ""
45
+ actor.run(grep, :once => true) do |ch, str, out|
46
+ result << out if str == :out
47
+ raise "could not determine current revision" if str == :err
48
+ end
49
+
50
+ date, time, user, rev, dir = result.split
51
+ raise "current revision not found in revisions.log" unless dir == latest
52
+ rev.to_i
53
+ end
54
+
55
+ # Return a string containing the diff between the two revisions. +from+
56
+ # and +to+ may be in any format that bzr recognizes as a valid revision
57
+ # identifier. If +from+ is +nil+, it defaults to the last deployed
58
+ # revision. If +to+ is +nil+, it defaults to the last developed revision.
59
+ def diff(actor, from=nil, to=nil)
60
+ from ||= current_revision(actor)
61
+ to ||= latest_revision
62
+ from = baz_revision_name(from)
63
+ to = baz_revision_name(to)
64
+ `#{baz} delta --diffs -A #{baz_archive} #{baz_version}--#{from} #{baz_version}--#{to}`
65
+ end
66
+
67
+ # Check out (on all servers associated with the current task) the latest
68
+ # revision. Uses the given actor instance to execute the command.
69
+ def checkout(actor)
70
+ op = configuration[:checkout] || "get"
71
+ from = baz_revision_name(configuration.revision)
72
+ command = "#{baz} #{op} #{configuration.repository}--#{from} #{actor.release_path} &&"
73
+ run_checkout(actor, command, &baz_stream_handler(actor))
74
+ end
75
+
76
+ def update(actor)
77
+ command = "cd #{actor.current_path} && #{baz} update &&"
78
+ run_update(actor, command, &baz_stream_handler(actor))
79
+ end
80
+
81
+ private
82
+ def baz
83
+ configuration[:baz] || "baz"
84
+ end
85
+
86
+ def baz_revision_name(number)
87
+ if number.to_i == 0 then
88
+ "base-0"
89
+ else
90
+ "patch-#{number}"
91
+ end
92
+ end
93
+
94
+ def baz_archive
95
+ configuration[:repository][/(.*)\//, 1]
96
+ end
97
+
98
+ def baz_version
99
+ configuration[:repository][/\/(.*)$/, 1]
100
+ end
101
+
102
+ def baz_stream_handler(actor)
103
+ Proc.new do |ch, stream, out|
104
+ prefix = "#{stream} :: #{ch[:host]}"
105
+ actor.logger.info out, prefix
106
+ if out =~ /\bpassword.*:/i
107
+ actor.logger.info "baz is asking for a password", prefix
108
+ ch.send_data "#{actor.password}\n"
109
+ elsif out =~ %r{passphrase}
110
+ message = "baz needs your key's passphrase, sending empty string"
111
+ actor.logger.info message, prefix
112
+ ch.send_data "\n"
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,70 @@
1
+ require 'switchtower/scm/base'
2
+
3
+ module SwitchTower
4
+ module SCM
5
+
6
+ # An SCM module for using Bazaar-NG (bzr) as your source control tool.
7
+ # You can use it by placing the following line in your configuration:
8
+ #
9
+ # set :scm, :bzr
10
+ #
11
+ # Also, this module accepts a <tt>:bzr</tt> configuration variable,
12
+ # which (if specified) will be used as the full path to the bzr
13
+ # executable on the remote machine:
14
+ #
15
+ # set :bzr, "/opt/local/bin/bzr"
16
+ class Bzr < Base
17
+ # Return an integer identifying the last known revision in the bzr
18
+ # repository. (This integer is currently the revision number.)
19
+ def latest_revision
20
+ `#{bzr} revno #{configuration.repository}`.to_i
21
+ end
22
+
23
+ # Return the number of the revision currently deployed.
24
+ def current_revision(actor)
25
+ command = "#{bzr} revno #{actor.release_path} &&"
26
+ run_update(actor, command, &bzr_stream_handler(actor))
27
+ end
28
+
29
+ # Return a string containing the diff between the two revisions. +from+
30
+ # and +to+ may be in any format that bzr recognizes as a valid revision
31
+ # identifier. If +from+ is +nil+, it defaults to the last deployed
32
+ # revision. If +to+ is +nil+, it defaults to the last developed revision.
33
+ # Pay attention to the fact that as of now bzr does NOT support
34
+ # diff on remote locations.
35
+ def diff(actor, from=nil, to=nil)
36
+ from ||= current_revision(actor)
37
+ to ||= ""
38
+ `#{bzr} diff -r #{from}..#{to} #{configuration.repository}`
39
+ end
40
+
41
+ # Check out (on all servers associated with the current task) the latest
42
+ # revision. Uses the given actor instance to execute the command. If
43
+ # bzr asks for a password this will automatically provide it (assuming
44
+ # the requested password is the same as the password for logging into the
45
+ # remote server.)
46
+ def checkout(actor)
47
+ op = configuration[:checkout] || "branch"
48
+ command = "#{bzr} #{op} -r#{configuration.revision} #{configuration.repository} #{actor.release_path} &&"
49
+ run_checkout(actor, command, &bzr_stream_handler(actor))
50
+ end
51
+
52
+ def update(actor)
53
+ command = "cd #{actor.current_path} && #{bzr} pull -q &&"
54
+ run_update(actor, command, &bzr_stream_handler(actor))
55
+ end
56
+
57
+ private
58
+ def bzr
59
+ configuration[:bzr] || "bzr"
60
+ end
61
+
62
+ def bzr_stream_handler(actor)
63
+ Proc.new do |ch, stream, out|
64
+ prefix = "#{stream} :: #{ch[:host]}"
65
+ actor.logger.info out, prefix
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -19,10 +19,27 @@ module SwitchTower
19
19
  # the revisions, etc.) via the <tt>:local</tt> variable, which defaults to
20
20
  # ".".
21
21
  #
22
+ # You may also specify a <tt>:branch</tt> configuration variable,
23
+ # which (if specified) will be used in the '-r' option to the cvs
24
+ # check out command. If it is not set, the module will determine if a
25
+ # branch is being used in the CVS sandbox relative to
26
+ # <tt>:local</tt> and act accordingly.
27
+ #
28
+ # set :branch, "prod-20060124"
29
+ #
22
30
  # Also, you can specify the CVS_RSH variable to use on the remote machine(s)
23
31
  # via the <tt>:cvs_rsh</tt> variable. This defaults to the value of the
24
32
  # CVS_RSH environment variable locally, or if it is not set, to "ssh".
25
33
  class Cvs < Base
34
+ def initialize(configuration)
35
+ super(configuration)
36
+ if not configuration.respond_to?(:branch) then
37
+ configuration.set(:branch) { self.current_branch }
38
+ else
39
+ @current_branch = configuration[:branch]
40
+ end
41
+ end
42
+
26
43
  # Return a string representing the date of the last revision (CVS is
27
44
  # seriously retarded, in that it does not give you a way to query when
28
45
  # the last revision was made to the repository, so this is a fairly
@@ -30,22 +47,37 @@ module SwitchTower
30
47
  def latest_revision
31
48
  return @latest_revision if @latest_revision
32
49
  configuration.logger.debug "querying latest revision..."
33
- @latest_revision = cvs_log(configuration.local).
50
+ @latest_revision = cvs_log(cvs_local, configuration.branch).
34
51
  split(/\r?\n/).
35
- grep(/^date: (.*?);/) { Time.parse($1).strftime("%F %T") }.
52
+ grep(/^date: (.*?);/) { Time.parse($1).strftime("%Y-%m-%d %H:%M:%S") }.
36
53
  sort.
37
54
  last
38
55
  end
39
56
 
57
+ # Return a string representing the branch that the sandbox
58
+ # relative to <tt>:local</tt> contains.
59
+ def current_branch
60
+ return @current_branch if @current_branch
61
+ configuration.logger.debug "determining current_branch..."
62
+ @current_branch = cvs_branch(cvs_local)
63
+ end
64
+
40
65
  # Check out (on all servers associated with the current task) the latest
41
- # revision. Uses the given actor instance to execute the command.
66
+ # revision, using a branch if necessary. Uses the given actor instance
67
+ # to execute the command.
42
68
  def checkout(actor)
43
69
  cvs = configuration[:cvs] || "cvs"
44
70
  cvs_rsh = configuration[:cvs_rsh] || ENV['CVS_RSH'] || "ssh"
45
71
 
72
+ if "HEAD" == configuration.branch then
73
+ branch_option = ""
74
+ else
75
+ branch_option = "-r #{configuration.branch}"
76
+ end
77
+
46
78
  command = <<-CMD
47
79
  cd #{configuration.releases_path};
48
- CVS_RSH="#{cvs_rsh}" #{cvs} -d #{configuration.repository} -Q co -D "#{configuration.revision}" -d #{File.basename(actor.release_path)} #{actor.application};
80
+ CVS_RSH="#{cvs_rsh}" #{cvs} -d #{configuration.repository} -Q co -D "#{configuration.revision}" #{branch_option} -d #{File.basename(actor.release_path)} #{actor.application};
49
81
  CMD
50
82
 
51
83
  run_checkout(actor, command) do |ch, stream, out|
@@ -64,8 +96,27 @@ module SwitchTower
64
96
 
65
97
  private
66
98
 
67
- def cvs_log(path)
68
- `cd #{path || "."} && cvs -q log -N -rHEAD`
99
+ # Look for a 'CVS/Tag' file in the path. If this file exists
100
+ # and contains a Line starting with 'T' then this CVS sandbox is
101
+ # 'tagged' with a branch. In the default case return 'HEAD'
102
+ def cvs_branch(path)
103
+ branch = "HEAD"
104
+ branch_file = File.join(path || ".", "CVS", "Tag")
105
+ if File.exists?(branch_file) then
106
+ File.open(branch_file) do |f|
107
+ possible_branch = f.find { |l| l =~ %r{^T} }
108
+ branch = possible_branch.strip[1..-1] if possible_branch
109
+ end
110
+ end
111
+ branch
112
+ end
113
+
114
+ def cvs_log(path,branch)
115
+ `cd #{path || "."} && cvs -q log -N -r#{branch}`
116
+ end
117
+
118
+ def cvs_local
119
+ configuration.local || "."
69
120
  end
70
121
  end
71
122
 
@@ -0,0 +1,139 @@
1
+ require 'switchtower/scm/base'
2
+
3
+ module SwitchTower
4
+ module SCM
5
+
6
+ # An SCM module for using perforce as your source control tool.
7
+ # This module can explicitly selected by placing the following line
8
+ # in your configuration:
9
+ #
10
+ # set :scm, :perforce
11
+ #
12
+ # Also, this module accepts a <tt>:p4</tt> configuration variable,
13
+ # which (if specified) will be used as the full path to the p4
14
+ # executable on the remote machine:
15
+ #
16
+ # set :p4, "/usr/local/bin/p4"
17
+ #
18
+ # This module accepts another <tt>:p4sync_flags</tt> configuration
19
+ # variable, which (if specified) can add extra options. This setting
20
+ # defaults to the value "-f" which forces resynchronization.
21
+ #
22
+ # set :p4sync_flags, "-f"
23
+ #
24
+ # This module accepts another <tt>:p4client_root</tt> configuration
25
+ # variable to handle mapping adjustments. Perforce doesn't have the
26
+ # ability to sync to a specific directory (e.g. the release_path); the
27
+ # location being synced to is defined by the client-spec. As such, we
28
+ # sync the client-spec (defined by <tt>p4client</tt> and then copy from
29
+ # the determined root of the client-spec mappings to the release_path.
30
+ # In the unlikely event that your client-spec mappings introduces
31
+ # directory structure above the rails structure, you can override the
32
+ # may need to specify the directory
33
+ #
34
+ # set :p4client_root, "/user/rmcmahon/project1/code"
35
+ #
36
+ # Finally, the module accepts a <tt>p4diff2_options</tt> configuration
37
+ # variable. This can be used to manipulate the output when running a
38
+ # diff between what is deployed, and another revision. This option
39
+ # defaults to "-u". Run 'p4 help diff2' for other options.
40
+ #
41
+ class Perforce < Base
42
+
43
+ def latest_revision
44
+ configuration.logger.debug "querying latest revision..." unless @latest_revision
45
+ @latest_revision = `#{local_p4} counter change`.strip
46
+ @latest_revision
47
+ end
48
+
49
+ # Return the number of the revision currently deployed.
50
+ def current_revision(actor)
51
+ latest = actor.releases.last
52
+ grep = %(grep " #{latest}$" #{configuration.deploy_to}/revisions.log)
53
+ result = ""
54
+ actor.run(grep, :once => true) do |ch, str, out|
55
+ result << out if str == :out
56
+ raise "could not determine current revision" if str == :err
57
+ end
58
+
59
+ date, time, user, rev, dir = result.split
60
+ raise "current revision not found in revisions.log" unless dir == latest
61
+ rev.to_i
62
+ end
63
+
64
+ # Return a string containing the diff between the two revisions. +from+
65
+ # and +to+ may be in any format that p4 recognizes as a valid revision
66
+ # identifiers. If +from+ is +nil+, it defaults to the last deployed
67
+ # revision. If +to+ is +nil+, it defaults to #head.
68
+ def diff(actor, from=nil, to=nil)
69
+ from ||= "@#{current_revision(actor)}"
70
+ to ||= "#head"
71
+ p4client = configuration[:p4client]
72
+ p4diff2_options = configuration[:p4diff2_options]||"-u -db"
73
+ `#{local_p4} diff2 #{p4diff2_options} //#{p4client}/...#{from} //#{p4client}/...#{to}`
74
+ end
75
+
76
+ # Syncronizes (on all servers associated with the current task) the head
77
+ # revision of the code. Uses the given actor instance to execute the command.
78
+ #
79
+ def checkout(actor)
80
+ p4sync_flags = configuration[:p4sync_flags] || "-f"
81
+ p4client_root = configuration[:p4client_root] || "`#{remote_p4} client -o | grep ^Root | cut -f2`"
82
+ command = "#{remote_p4} sync #{p4sync_flags} && cp -rf #{p4client_root} #{actor.release_path};"
83
+ run_checkout(actor, command, &p4_stream_handler(actor))
84
+ end
85
+
86
+ def update(actor)
87
+ raise "#{self.class} doesn't support update(actor)"
88
+ end
89
+
90
+ private
91
+
92
+ def local_p4
93
+ add_standard_p4_options('p4')
94
+ end
95
+
96
+ def remote_p4
97
+ p4_cmd = configuration[:p4] || 'p4'
98
+ add_standard_p4_options(p4_cmd)
99
+ end
100
+
101
+ def add_standard_p4_options(p4_location)
102
+ check_settings
103
+ p4_cmd = p4_location
104
+ p4_cmd = "#{p4_cmd} -p #{configuration[:p4port]}" if configuration[:p4port]
105
+ p4_cmd = "#{p4_cmd} -u #{configuration[:p4user]}" if configuration[:p4user]
106
+ p4_cmd = "#{p4_cmd} -P #{configuration[:p4passwd]}" if configuration[:p4passwd]
107
+ p4_cmd = "#{p4_cmd} -c #{configuration[:p4client]}" if configuration[:p4client]
108
+ p4_cmd
109
+ end
110
+
111
+ def check_settings
112
+ check_setting(:p4port, "Add set :p4port, <your perforce server details e.g. my.p4.server:1666> to deploy.rb")
113
+ check_setting(:p4user, "Add set :p4user, <your production build username> to deploy.rb")
114
+ check_setting(:p4passwd, "Add set :p4passwd, <your build user password> to deploy.rb")
115
+ check_setting(:p4client, "Add set :p4client, <your client-spec name> to deploy.rb")
116
+ end
117
+
118
+ def check_setting(p4setting, message)
119
+ raise "#{p4setting} is not configured. #{message}" unless configuration[p4setting]
120
+ end
121
+
122
+ def p4_stream_handler(actor)
123
+ Proc.new do |ch, stream, out|
124
+ prefix = "#{stream} :: #{ch[:host]}"
125
+ actor.logger.info out, prefix
126
+ if out =~ /\(P4PASSWD\) invalid or unset\./i
127
+ raise "p4passwd is incorrect or unset"
128
+ elsif out =~ /Can.t create a new user.*/i
129
+ raise "p4user is incorrect or unset"
130
+ elsif out =~ /Perforce client error\:/i
131
+ raise "p4port is incorrect or unset"
132
+ elsif out =~ /Client \'[\w\-\_\.]+\' unknown.*/i
133
+ raise "p4client is incorrect or unset"
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -68,7 +68,8 @@ module SwitchTower
68
68
  # remote server.)
69
69
  def checkout(actor)
70
70
  op = configuration[:checkout] || "co"
71
- command = "#{svn} #{op} -q -r#{configuration.revision} #{configuration.repository} #{actor.release_path} &&"
71
+ username = configuration[:svn_username] ? "--username #{configuration[:svn_username]}" : ""
72
+ command = "#{svn} #{op} #{username} -q -r#{configuration.revision} #{configuration.repository} #{actor.release_path} &&"
72
73
  run_checkout(actor, command, &svn_stream_handler(actor))
73
74
  end
74
75
 
@@ -88,14 +89,18 @@ module SwitchTower
88
89
  def svn_log(path)
89
90
  `svn log -q -rhead #{path}`
90
91
  end
91
-
92
+
93
+ def svn_password
94
+ configuration[:svn_password] || configuration[:password]
95
+ end
96
+
92
97
  def svn_stream_handler(actor)
93
98
  Proc.new do |ch, stream, out|
94
99
  prefix = "#{stream} :: #{ch[:host]}"
95
100
  actor.logger.info out, prefix
96
101
  if out =~ /\bpassword.*:/i
97
102
  actor.logger.info "subversion is asking for a password", prefix
98
- ch.send_data "#{actor.password}\n"
103
+ ch.send_data "#{svn_password}\n"
99
104
  elsif out =~ %r{\(yes/no\)}
100
105
  actor.logger.info "subversion is asking whether to connect or not",
101
106
  prefix