redmine_stagecoach 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use --create ruby-1.9.2-p290@stagecoach
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'ghi'
4
+ gem 'trollop'
5
+ gem 'capistrano'
6
+
7
+ # Add dependencies to develop your gem here.
8
+ # Include everything needed to run rake, tests, features, etc.
9
+ group :development do
10
+ gem "shoulda", ">= 0"
11
+ gem "bundler", "~> 1.0.0"
12
+ gem "jeweler", "~> 1.6.4"
13
+ gem "rcov", ">= 0"
14
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,39 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ capistrano (2.9.0)
5
+ highline
6
+ net-scp (>= 1.0.0)
7
+ net-sftp (>= 2.0.0)
8
+ net-ssh (>= 2.0.14)
9
+ net-ssh-gateway (>= 1.1.0)
10
+ ghi (0.3.0)
11
+ git (1.2.5)
12
+ highline (1.6.9)
13
+ jeweler (1.6.4)
14
+ bundler (~> 1.0)
15
+ git (>= 1.2.5)
16
+ rake
17
+ net-scp (1.0.4)
18
+ net-ssh (>= 1.99.1)
19
+ net-sftp (2.0.5)
20
+ net-ssh (>= 2.0.9)
21
+ net-ssh (2.3.0)
22
+ net-ssh-gateway (1.1.0)
23
+ net-ssh (>= 1.99.1)
24
+ rake (0.9.2.2)
25
+ rcov (0.9.11)
26
+ shoulda (2.11.3)
27
+ trollop (1.16.2)
28
+
29
+ PLATFORMS
30
+ ruby
31
+
32
+ DEPENDENCIES
33
+ bundler (~> 1.0.0)
34
+ capistrano
35
+ ghi
36
+ jeweler (~> 1.6.4)
37
+ rcov
38
+ shoulda
39
+ trollop
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Oli Barnett
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,69 @@
1
+ = Stagecoach
2
+ stagecoach works in two stages, init and deploy. The init stage creates a branch based on a redmine/planio issue, and the deploy stage pushes, merges and (yes!) deploys it.
3
+
4
+ *You should always run stagecoach from the root directory of your repo*
5
+ Otherwise it may (will) break.
6
+
7
+ The first time you run stagecoach it will ask you for some information, namely your redmine/planio repo URL and your API key for this repo.
8
+ It will also install a custom commit-msg git hook (if you ask it to) - for more information, see below.
9
+
10
+ All stagecoach config is saved in /path/to/your/repo/.stagecoach which is created at initial setup and added to your global .gitignore. This is a yaml file with fairly obvious syntax
11
+ so if you need to remove a branch or edit the issue number that a branch points to, it is possible (although not necessarily recommended) to edit it.
12
+
13
+ Init Stage
14
+ ==========
15
+ stagecoach -p[lanio] 4115 (OR) -g[ithub] 525 -b[ranch] my_new_branch
16
+
17
+ You can also just run stagecoach without any flags and it will allow you to enter this stuff manually.
18
+
19
+ To get started, all stagecoach needs from you is the issue number you are working on (redmine/planio or github) and a new branch name. You /can/ use an existing branch if it
20
+ is up to date with your master branch. If it is not, stagecoach will squawk and die and you will have to bring the branch up to date, or use a new one.
21
+
22
+ If you are working from a redmine/planio issue, stagecoach sets the issue to 'In Progress'. Currently it does not assign the issue to you, but you have the option to view the issue
23
+ in your browser and do this manually. It also sets up a git issue for you to reference in your commits (see commit-msg githook).
24
+
25
+ If you are working from a github issue, we can all get on with our lives.
26
+
27
+ Coding Stage
28
+ ============
29
+ Future versions of stagecoach may do the coding for you, but at the moment you have to do this part manually.
30
+ Just code and commit, code and commit until your feature or fix is ready.
31
+
32
+ Commit-msg githook
33
+ ------------------
34
+ If you opt to install the commit-msg githook during initial setup (stagecoach -s) then your commit messages will be automatically referenced to the github issue of the branch you are in
35
+ (this only applies to branches created or registered in Stagecoach).
36
+
37
+ - to reference a different issue from a commit, simply refer to it as normal with #xxx in the commit message. The git-hook will leave your message alone.
38
+ - to make no reference at all, you can use the #noref tag in the commit message. The git-hook
39
+ - to close an issue with a commit, use the #closes tag.
40
+
41
+ For more information, see
42
+ - the githook itself at /path/to/your/repo/.git/hooks/commit-msg
43
+ - http://book.git-scm.com/5_git_hooks.html
44
+
45
+ Deploy Stage
46
+ ============
47
+ stagecoach -d[eploy]
48
+
49
+ This automates the entire deploy workflow for you as follows:
50
+
51
+ git push origin new_branch_name
52
+ git checkout staging
53
+ git pull
54
+ git merge task_name
55
+ git push origin staging
56
+ cap staging deploy
57
+ set redmine/planio ticket to 'feedback' status (if applicable)
58
+
59
+ Sample usage:
60
+ stagecoach -p 4115 -b new_branch_name
61
+ [code, commit until feature or fix is complete]
62
+ stagecoach -d
63
+
64
+ Flags
65
+ --branch, -b: Enter your new branch name here, eg. stagecoach -b new_branch (optional)
66
+ --planio, -p: Enter your planio issue number here, eg. stagecoach -p 1234 (optional)
67
+ --github, -g: Enter your github issue number here, eg. stagecoach -g 1234 (optional)
68
+ --deploy, -d: Use this option to skip straight to push & deploy if you have already pulled from master and created your new branch
69
+ --setup, -s: Use this the first time you run stagecoach to save your redmine repository/api key and install the commit-msg githook if desired
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "redmine_stagecoach"
18
+ gem.homepage = "http://github.com/omnikron/stagecoach"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Stagecoach is in ur Redmine, automating ur Git workflow.}
21
+ gem.description = %Q{Git/capistrano workflow automation script with Redmine & Github issue integration}
22
+ gem.email = "o.barnett@digitaleseiten.de"
23
+ gem.authors = ["Oli Barnett"]
24
+ gem.executables = ['stagecoach']
25
+ # dependencies defined in Gemfile
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rake/testtask'
30
+ Rake::TestTask.new(:test) do |test|
31
+ test.libs << 'lib' << 'test'
32
+ test.pattern = 'test/**/test_*.rb'
33
+ test.verbose = true
34
+ end
35
+
36
+ require 'rcov/rcovtask'
37
+ Rcov::RcovTask.new do |test|
38
+ test.libs << 'test'
39
+ test.pattern = 'test/**/test_*.rb'
40
+ test.verbose = true
41
+ test.rcov_opts << '--exclude "gems/*"'
42
+ end
43
+
44
+ task :default => :test
45
+
46
+ require 'rake/rdoctask'
47
+ Rake::RDocTask.new do |rdoc|
48
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
49
+
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = "stagecoach #{version}"
52
+ rdoc.rdoc_files.include('README*')
53
+ rdoc.rdoc_files.include('lib/**/*.rb')
54
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.5.0
data/bin/stagecoach ADDED
@@ -0,0 +1,270 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ require File.dirname(__FILE__) + '/../lib/stagecoach.rb'
4
+
5
+ CONFIG_FILE = `pwd`.chomp + '/.stagecoach'
6
+
7
+ module Stagecoach
8
+ staging = 'staging'
9
+ master = 'master'
10
+
11
+ # Command line options courtesy of the Trollop gem.
12
+ opts = CommandLine.trollop
13
+
14
+ # Initial setup with -s flag.
15
+ if opts[:setup]
16
+ Config.setup
17
+ end
18
+
19
+ # Initial setup without -s flag.
20
+ unless File.exist?(CONFIG_FILE)
21
+ Config.setup
22
+ end
23
+
24
+ # Load config file to a hash.
25
+ config = Config.yaml_to_hash
26
+
27
+ # Checks validity of argument variables.
28
+ # Ignore these checks with -t flag.
29
+ unless opts[:testing_given]
30
+ Config.setup if config["redmine_site"] == "none"
31
+ Config.setup if config["redmine_api"] == "none"
32
+
33
+ # Checks that command-line args are present and correct.
34
+ Trollop::die :planio, "issue number can only contain digits" if opts[:planio] && opts[:planio][/\D/]
35
+ Trollop::die :github, "issue number can only contain digits" if opts[:github] && opts[:github][/\D/]
36
+ Trollop::die :branch, "name must be longer than 1 character" if opts[:branch] && opts[:branch].length <= 1
37
+
38
+ # You can't give a planio and a github issue number (for the moment)
39
+ if opts[:github] && opts[:planio]
40
+ puts "You can't enter a github issue at the same time as a planio issue at the moment. Make up your mind!\nExiting..."
41
+ exit
42
+ end
43
+ end
44
+
45
+ # Set up redmine client config.
46
+ RedmineApi::Client.instance_eval do
47
+ self.site = config["redmine_site"]
48
+ self.user = config["redmine_api_key"]
49
+ end
50
+
51
+ # Checks for uncommitted/unstashed changes and aborts if present.
52
+ if Git.changes != ''
53
+ puts "You have uncommitted changes:".red
54
+ puts Git.changes
55
+ puts "Please commit or stash these changes before running Stagecoach. -h for help."
56
+ puts "Exiting..."
57
+ exit
58
+ end
59
+
60
+ # Initial stage - set up branch and git issue.
61
+
62
+ unless opts[:deploy]
63
+
64
+ # If no issue argument has been given.
65
+ if opts[:github].nil? && opts[:planio].nil?
66
+ print "Are you working on a [P]lanio or a [G]ithub issue: "
67
+ case STDIN.gets.chomp
68
+ when 'P'
69
+ print "Please enter your Planio issue number: "
70
+ opts[:planio] = gets.chomp
71
+ when 'G'
72
+ print "Please enter your Github issue number: "
73
+ opts[:github] = gets.chomp
74
+ end
75
+ end
76
+
77
+ # Check that the planio issue is not already assigned.
78
+ if opts[:planio]
79
+ planio_issue_number = opts[:planio]
80
+ planio_issue = Redmine.issue(planio_issue_number)
81
+ if planio_issue.status.id != '1'
82
+ puts "Warning!".red
83
+ puts "This issue is in status '#{planio_issue.status.name}'"
84
+ begin
85
+ puts "It is assigned to #{planio_issue.assigned_to.name}"
86
+ rescue
87
+ puts "But it is not assigned to anybody yet."
88
+ end
89
+ print "Continue? [Y]es or [Q]uit: "
90
+ case STDIN.gets.chomp
91
+ when 'Y'
92
+ when 'Q'
93
+ exit
94
+ end
95
+ end
96
+
97
+ # Set the planio issue status to 'In Bearbeitung'
98
+ planio_issue.status_id = 2
99
+ planio_issue.save
100
+ end
101
+
102
+ # TODO: Check that the github issue is not already assigned.
103
+
104
+ CommandLine.line_break
105
+ puts "Stagecoach: initial stage"
106
+
107
+ # Change to master, pull changes, and create a new branch.
108
+ CommandLine.line_break
109
+ puts "Switching to master branch"
110
+ #
111
+ # TODO if there is a file that has been git added but not git committed, it
112
+ # will pop up at this point looking confusing (eg. "A test_file").
113
+ # Handle this better?
114
+ #
115
+ Git.checkout(master)
116
+ puts "Pulling changes:"
117
+ Git.pull
118
+ if opts[:branch]
119
+ new_branch = opts[:branch]
120
+ else
121
+ print "Please enter a new git branch name for your changes (branch will be created): "
122
+ new_branch = STDIN.gets.chomp
123
+ end
124
+
125
+ # Check that the new branch isn't master, because that would be silly
126
+ case new_branch
127
+ when 'master', 'Master'
128
+ puts "You can't use stagecoach to deploy your master branch.\nExiting..."
129
+ exit
130
+ end
131
+
132
+ # Make sure new local branch does not already exist.
133
+ if Git.branch_exist?(new_branch)
134
+ puts "There is already a local branch called #{new_branch}."
135
+ if Git.diff(master, new_branch) == ""
136
+ print "#{new_branch} is up to date with master. [U]se or [Q]uit: "
137
+ else
138
+ puts "#{new_branch} is not up to date with master. Please use a different branch or update this one.".red
139
+ puts "Exiting..."
140
+ CommandLine.line_break
141
+ puts "The following files in branch '#{new_branch}' differ from their master branch versions:"
142
+ puts Git.diff(master, new_branch)
143
+ CommandLine.line_break
144
+ exit
145
+ end
146
+ case STDIN.gets.chomp
147
+ when 'U'
148
+ Git.change_to_branch(new_branch)
149
+ when 'Q'
150
+ exit
151
+ end
152
+ else
153
+ Git.new_branch(new_branch)
154
+ end
155
+
156
+ # Ugly code, pretty output...
157
+ CommandLine.line_break
158
+
159
+ # Issue handling.
160
+ if opts[:github]
161
+ config[Git.current_branch] = {:github_issue => opts[:github]}
162
+ #TODO check that github issue is not assigned to somebody already
163
+ elsif opts[:planio]
164
+ config[Git.current_branch] = {:planio_issue => planio_issue_number}
165
+ end
166
+
167
+ issue = config[Git.current_branch]
168
+
169
+ # Set up the related issue for this branch.
170
+ if planio_issue = issue[:planio_issue]
171
+ begin
172
+ puts "Searching for issue number #{planio_issue}..."
173
+ redmine_issue = Redmine.issue(planio_issue)
174
+ rescue ActiveResource::ResourceNotFound => e
175
+ puts e.message
176
+ exit
177
+ end
178
+ puts "Issue found: #{redmine_issue.subject}\n"
179
+
180
+ # Create a Github issue referencing the planio issue.
181
+ puts "Creating Git issue with subject: " + redmine_issue.subject
182
+ body = "Planio issue: #{Redmine.issue_url(redmine_issue)} \n\n #{redmine_issue.description}"
183
+
184
+ # Create a Git issue.
185
+ github_issue = Git.new_issue(redmine_issue.subject, body)
186
+ github_issue_id = github_issue[/\d+/]
187
+
188
+ # Save it so we can reference it in commits using the magic of git hooks!
189
+ config[Git.current_branch] = {:github_issue => github_issue_id, :planio_issue => planio_issue}
190
+
191
+ print "Would you like to edit the issue on Github? [Y]es or anything else to continue: "
192
+
193
+ if STDIN.gets.chomp == 'Y'
194
+ Git.view_issue(github_issue_id)
195
+ else
196
+ end
197
+ end
198
+
199
+ # Github issues are easier.
200
+ if issue[:github]
201
+ #TODO what happens if no github issue is found?
202
+ puts "Searching for github issue number #{issue[:number]}..."
203
+ github_issue = Git.issue(issue[:number])
204
+ puts "Issue found: #{github_issue} \n"
205
+ end
206
+
207
+ # Saves the branch-specific details for later.
208
+ Config.save(config)
209
+ puts "Happy coding! Run stagecoach -d when you're ready to deploy."
210
+ end
211
+
212
+ # ------------------------------------------------------------------
213
+ # Deploy stage.
214
+ if opts[:deploy]
215
+
216
+ # Get the current git branch
217
+ branch = Git.current_branch
218
+
219
+ # There's no point in deploying without any commits
220
+ unless Git.branch_has_commits?(branch)
221
+ puts "You don't have any uncommitted changes on branch #{branch}. Please make some commits before running stagecoach!\nExiting..."
222
+ exit
223
+ end
224
+
225
+ # You never know! Display git status in case there are any nasty surprises.
226
+ unless Git.status =~ /nothing to commit/
227
+ CommandLine.line_break
228
+ puts "You have a dirty git branch:\n".red
229
+ puts Git.status
230
+ CommandLine.line_break
231
+ print "[D]eploy anyway".red
232
+ print " or "
233
+ print "[anything else] to cancel: ".green
234
+ case STDIN.gets.chomp
235
+ when "D"
236
+ CommandLine.line_break
237
+ puts "DEPLOYING:"
238
+ CommandLine.line_break
239
+ else
240
+ puts "Exiting..."
241
+ exit
242
+ end
243
+ end
244
+
245
+
246
+ # Stop anybody deploying master to staging...
247
+ case branch
248
+ when 'master', 'Master'
249
+ puts "You can't use stagecoach to deploy your master branch.\nExiting..."
250
+ exit
251
+ end
252
+
253
+ # Finally, push, merge and deploy!
254
+ Git.push(branch)
255
+ Git.merge(staging, branch)
256
+ Git.push(staging)
257
+ Capistrano.deploy(staging)
258
+ Git.change_to_branch(master)
259
+
260
+ # Planio issue to feedback status
261
+ if planio_issue_number = config[branch][:planio_issue]
262
+ CommandLine.line_break
263
+ puts "Attempting to change Planio ticket status to 'Feedback' for you:"
264
+ issue = Redmine.issue(planio_issue_number)
265
+ issue.status_id = 4
266
+ issue.save
267
+ Redmine.view_issue(issue)
268
+ end
269
+ end
270
+ end
data/lib/.DS_Store ADDED
Binary file
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Git commit-msg hook. If your branch name is in the form "t123", automatically
4
+ # adds "Refs #123." to commit messages unless they mention "#123" already.
5
+ # Include "#close" or "#finish" to add "Closes #123."
6
+ #
7
+ # For Pivotal Tracker, branch names like "s123" adds "[#123]".
8
+ # Include "#close" or "#finish" to add "[Finishes #123]".
9
+ #
10
+ # If you include "#noref" in the commit message, nothing will be added to
11
+ # the commit message, and the "#noref" itself will be stripped.
12
+ #
13
+ # By Henrik Nyh <http://henrik.nyh.se> 2009-09-10 under the MIT License.
14
+ #
15
+ #
16
+ # Install:
17
+ #
18
+ # cd your_project
19
+ # stick it in .git/hooks/commit-msg && chmod u+x .git/hooks/commit-msg
20
+ #
21
+ # Or store it centrally and symlink in your projects:
22
+ # TODO Replace ~./.githooks etc with the file location in the gem
23
+ # ~/.githooks/commit-msg && chmod u+x ~/.githooks/commit-msg
24
+ # cd your_project
25
+ # ~/.githooks/commit-msg .git/hooks
26
+
27
+ require 'yaml'
28
+
29
+ # Custom method to check for installed redmine_stagecoach gem
30
+ def gem_available?(name)
31
+ Gem::Specification.find_by_name(name)
32
+ rescue Gem::LoadError
33
+ false
34
+ rescue
35
+ Gem.available?(name)
36
+ end
37
+
38
+ if gem_available?('redmine_stagecoach') == false
39
+ exit
40
+ else
41
+ config = YAML::load(File.open(File.dirname(__FILE__) + '/../../.stagecoach', 'r'))
42
+ end
43
+
44
+ # Find out what branch we are on
45
+ def branches
46
+ `git branch`.split("\n")
47
+ end
48
+
49
+ def current_branch
50
+ branches.each do |b|
51
+ if b =~ /\*/
52
+ return b[1..-1].strip
53
+ end
54
+ end
55
+ end
56
+
57
+
58
+ # And now the git hook stuff
59
+
60
+ FLAGS = [
61
+ NOREF = "noref",
62
+ UP_NOREF = "Noref",
63
+ CAPS_NOREF = "NOREF",
64
+ CLOSE = "close",
65
+ UP_CLOSE= "Close",
66
+ CAPS_CLOSE= "CLOSE"
67
+ ]
68
+
69
+ NO_REFERENCE_FLAGS = [ NOREF, UP_NOREF, CAPS_NOREF ]
70
+ CLOSING_FLAGS = [ CLOSE, UP_CLOSE, CAPS_CLOSE ]
71
+
72
+ begin
73
+ ticket_number = config[current_branch][:github_issue]
74
+ rescue
75
+ exit
76
+ end
77
+ finish = "Closes #%s" % ticket_number
78
+ reference = "#%s" % ticket_number
79
+
80
+ message_file = ARGV[0]
81
+ message = File.read(message_file).strip
82
+ exit if message.include?("##{ticket_number}")
83
+ exit if message =~ /#\d+/
84
+
85
+ # Determine if any of the flags are included. Make a note of which and then remove it.
86
+ message.sub!(/(?:^|\s)#(#{Regexp.union(*FLAGS)})\b/, '')
87
+ flag = $1
88
+
89
+ message =
90
+ case flag
91
+ when *NO_REFERENCE_FLAGS
92
+ message
93
+ when *CLOSING_FLAGS
94
+ [ message, finish ].join(" ")
95
+ else
96
+ [ message, reference ].join(" ")
97
+ end
98
+
99
+ File.open(message_file, 'w') {|f| f.write message }
data/lib/stagecoach.rb ADDED
@@ -0,0 +1,7 @@
1
+ lib = File.dirname(__FILE__)
2
+
3
+ require lib + '/stagecoach/config'
4
+ require lib + '/stagecoach/git'
5
+ require lib + '/stagecoach/redmine'
6
+ require lib + '/stagecoach/command_line.rb'
7
+ require lib + '/stagecoach/capistrano.rb'
@@ -0,0 +1,11 @@
1
+ module Stagecoach
2
+ class Capistrano
3
+ class << self
4
+ def deploy(branch)
5
+ CommandLine.line_break
6
+ puts "Deploying staging"
7
+ puts `bundle exec cap #{branch} deploy`
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,84 @@
1
+ module Stagecoach
2
+ class CommandLine
3
+ def self.line_break
4
+ puts "\n"
5
+ end
6
+
7
+ def self.trollop
8
+ require 'trollop'
9
+ # Command line options using Trollop.
10
+ Trollop::options do
11
+ banner <<-EOS
12
+ stagecoach works in two stages, init and deploy. The init stage creates a branch based on a redmine/planio issue, and the deploy stage pushes, merges and (yes!) deploys it.
13
+
14
+ #{"You should always run stagecoach from the root directory of your repo".red}
15
+ Otherwise it may (will) break.
16
+
17
+ The first time you run stagecoach it will ask you for some information, namely your redmine/planio repo URL and your API key for this repo.
18
+ It will also install a custom commit-msg git hook (if you ask it to) - for more information, see below.
19
+
20
+ All stagecoach config is saved in /path/to/your/repo/.stagecoach which is created at initial setup and added to your global .gitignore. This is a yaml file with fairly obvious syntax
21
+ so if you need to remove a branch or edit the issue number that a branch points to, it is possible (although not necessarily recommended) to edit it.
22
+
23
+ Init Stage
24
+ ----------
25
+ stagecoach -p[lanio] 4115 (OR) -g[ithub] 525 -b[ranch] my_new_branch
26
+
27
+ You can also just run stagecoach without any flags and it will allow you to enter this stuff manually.
28
+
29
+ To get started, all stagecoach needs from you is the issue number you are working on (redmine/planio or github) and a new branch name. You /can/ use an existing branch if it
30
+ is up to date with your master branch. If it is not, stagecoach will squawk and die and you will have to bring the branch up to date, or use a new one.
31
+
32
+ If you are working from a redmine/planio issue, stagecoach sets the issue to 'In Progress'. Currently it does not assign the issue to you, but you have the option to view the issue
33
+ in your browser and do this manually. It also sets up a git issue for you to reference in your commits (see commit-msg githook).
34
+
35
+ If you are working from a github issue, we can all get on with our lives.
36
+
37
+ Coding Stage
38
+ ------------
39
+ Future versions of stagecoach may do the coding for you, but at the moment you have to do this part manually.
40
+ Just code and commit, code and commit until your feature or fix is ready.
41
+
42
+ #{"commit-msg githook".green}
43
+ If you opt to install the commit-msg githook during initial setup (stagecoach -s) then your commit messages will be automatically referenced to the github issue of the branch you are in
44
+ (this only applies to branches created or registered in Stagecoach).
45
+
46
+ - to reference a different issue from a commit, simply refer to it as normal with #xxx in the commit message. The git-hook will leave your message alone.
47
+ - to make no reference at all, you can use the #noref tag in the commit message. The git-hook
48
+ - to close an issue with a commit, use the #closes tag.
49
+
50
+ For more information, see
51
+ - the githook itself at /path/to/your/repo/.git/hooks/commit-msg
52
+ - http://book.git-scm.com/5_git_hooks.html
53
+
54
+ Deploy Stage
55
+ ------------
56
+ stagecoach -d[eploy]
57
+
58
+ This automates the entire deploy workflow for you as follows:
59
+
60
+ git push origin new_branch_name
61
+ git checkout staging
62
+ git pull
63
+ git merge task_name
64
+ git push origin staging
65
+ cap staging deploy
66
+ set redmine/planio ticket to 'feedback' status (if applicable)
67
+
68
+ #{"Sample usage:".green}
69
+ stagecoach -p 4115 -b new_branch_name
70
+ [code, commit until feature or fix is complete]
71
+ stagecoach -d
72
+
73
+ #{"Flags".red}
74
+ EOS
75
+ opt :branch, "Enter your new branch name here, eg. stagecoach -b new_branch (optional)", :type => :string
76
+ opt :planio, "Enter your planio issue number here, eg. stagecoach -p 1234 (optional)", :type => :string
77
+ opt :github, "Enter your github issue number here, eg. stagecoach -g 1234 (optional)", :type => :string
78
+ opt :deploy, "Use this option to skip straight to push & deploy if you have already pulled from master and created your new branch"
79
+ opt :setup, "Use this the first time you run stagecoach to save your redmine repository and api key"
80
+ opt :testing, "Dev testing tool"
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,125 @@
1
+ require 'yaml'
2
+ require 'FileUtils'
3
+
4
+ module Stagecoach
5
+ class Config
6
+ class << self
7
+ def new
8
+ File.open(CONFIG_FILE, 'w') { |f| f.write("---\nredmine_site: none\nredmine_api_key: none")}
9
+ end
10
+
11
+ def open
12
+ File.open(CONFIG_FILE, 'r+')
13
+ end
14
+
15
+ def yaml_to_hash
16
+ YAML::load(Config.open)
17
+ end
18
+
19
+ def save(hash, config_file = Config.open)
20
+ config_file.write(hash.to_yaml)
21
+ end
22
+
23
+ def githook_install(source_dir, install_dir, file)
24
+ FileUtils.cp(source_dir + file, install_dir + file)
25
+ puts 'OK!'
26
+ puts 'Making githook executable (may require admin password)'
27
+ FileUtils.chmod(0711, ( install_dir + file ))
28
+ puts 'OK!'
29
+ end
30
+
31
+ def setup
32
+ # Say hello
33
+ CommandLine.line_break
34
+ puts "Stagecoach Initial Setup"
35
+ CommandLine.line_break
36
+
37
+ # Now scare everybody away again
38
+ puts "You are running stagecoach from #{FileUtils.pwd.green}. Is this the root directory of your repository?"
39
+ puts "Stagecoach may not work properly anywhere else! So proceed with caution"
40
+ CommandLine.line_break
41
+ print "[C]ontinue or [Q]uit: "
42
+
43
+ # Create a config file if necessary
44
+ case STDIN.gets.chomp
45
+ when 'C'
46
+ Config.new unless File.exist?(CONFIG_FILE)
47
+ when 'Q'
48
+ puts "Exiting..."
49
+ exit
50
+ end
51
+
52
+ # Tell git to ignore the stagecoach config file
53
+ Git.global_ignore('.stagecoach')
54
+
55
+ # Install the commit-msg githook if it is not already there:
56
+ source_dir = (File.dirname(__FILE__) + '/../githooks/')
57
+ install_dir = FileUtils.pwd + '/.git/hooks/'
58
+ git_hook = 'commit-msg'
59
+
60
+ CommandLine.line_break
61
+ puts "Would you like to install the stagecoach #{"commit-msg githook".green}?"
62
+ puts "This automatically references stagecoach-created github issues from each commit you make"
63
+ puts "Note that this will only affect branches created in stagecoach. For more information run stagecoach -h"
64
+ CommandLine.line_break
65
+ print "[I]nstall or [S]kip this step: "
66
+ loop do
67
+ case STDIN.gets.chomp
68
+ when 'I'
69
+ if File.exist?(install_dir + git_hook)
70
+ case FileUtils.compare_file(source_dir + git_hook, install_dir + git_hook)
71
+ when true
72
+ puts 'The stagecoach githook is already installed in this repo. Skipping this step...'
73
+ break
74
+ when false
75
+ puts "You have a commit-msg githook already. Are you sure you want to install? This will #{'overwrite'.red} your current commit-msg githook."
76
+ print "Type [overwrite] to continue or anything else to skip installation: "
77
+ case STDIN.gets.chomp
78
+ when 'overwrite'
79
+ Config.githook_install(source_dir, install_dir, git_hook)
80
+ break
81
+ else
82
+ break
83
+ end
84
+ end
85
+ else
86
+ puts "Installing..."
87
+ Config.githook_install(source_dir, install_dir, git_hook)
88
+ break
89
+ end
90
+ when 'S'
91
+ puts 'Skipping Installation.'
92
+ break
93
+ end
94
+ end
95
+
96
+ # TODO Some verification of the input at this stage, for example test the
97
+ # connection and have the user re-enter the details if necessary
98
+ # http://api.rubyonrails.org/classes/ActiveResource/Connection.html#method-i-head
99
+ loop do
100
+ CommandLine.line_break
101
+ print "Enter your redmine/planio repository, eg. https://digitaleseiten.plan.io: "
102
+ redmine_repo = STDIN.gets.chomp
103
+ print "Enter your API key for that repo: "
104
+ redmine_api_key = STDIN.gets.chomp
105
+
106
+ Config.save({"redmine_site" => redmine_repo, "redmine_api_key" => redmine_api_key})
107
+
108
+ CommandLine.line_break
109
+ puts "Settings saved OK:"
110
+ puts "Repository: " + redmine_repo
111
+ puts "API Key: " + redmine_api_key
112
+ CommandLine.line_break
113
+ puts "Exiting..."
114
+ exit
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ class String
122
+ def red; colorize(self, "\e[1m\e[31m"); end
123
+ def green; colorize(self, "\e[32m"); end
124
+ def colorize(text, color_code) "#{color_code}#{text}\e[0m" end
125
+ end
@@ -0,0 +1,123 @@
1
+ module Stagecoach
2
+ class Git
3
+ class << self
4
+ def branches
5
+ `git branch`.split("\n")
6
+ end
7
+
8
+ def global_ignore(filename)
9
+ gitignore = File.open(Dir.home + '/.gitignore', 'a+')
10
+
11
+ # Check if filename is ignored already and ignores it if not
12
+ unless File.read(gitignore) =~ /\.stagecoach/
13
+ gitignore.puts(filename)
14
+ end
15
+ end
16
+
17
+ def changes
18
+ `git diff-files --name-status -r --ignore-submodules`
19
+ end
20
+
21
+ def status
22
+ `git status`
23
+ end
24
+
25
+ def current_branch
26
+ branches.each do |b|
27
+ if b =~ /\*/
28
+ return b[1..-1].strip
29
+ end
30
+ end
31
+ end
32
+
33
+ def correct_branch?
34
+ CommandLine.line_break
35
+ print "You are currently in local branch: #{Git.current_branch.red} \nAre these details correct? ([Y]es or [Q]uit): "
36
+ case STDIN.gets.chomp
37
+ when "Y"
38
+ when "Q"
39
+ exit
40
+ else
41
+ puts "Please enter Y to continue or Q to quit."
42
+ end
43
+ end
44
+
45
+ def new_branch(branch)
46
+ CommandLine.line_break
47
+ `git checkout -b #{branch}`
48
+ end
49
+
50
+ def change_to_branch(branch)
51
+ CommandLine.line_break
52
+ puts "Changing to branch '#{branch}'"
53
+ if branch_exist?(branch)
54
+ `git checkout #{branch}`
55
+ else
56
+ print "Branch '#{branch}' does not exist. [C]reate or [Q]uit: "
57
+ case STDIN.gets.chomp
58
+ when 'C'
59
+ new_branch(branch)
60
+ when 'Q'
61
+ exit
62
+ end
63
+ end
64
+ end
65
+
66
+ def diff(branch1, branch2)
67
+ diff = `git diff --name-status #{branch1}..#{branch2}`
68
+ return diff
69
+ end
70
+
71
+
72
+ def merge(to_branch, from_branch)
73
+ CommandLine.line_break
74
+ puts "Merging into #{to_branch} (after pulling updates)"
75
+ Git.change_to_branch(to_branch)
76
+ puts `git pull origin #{to_branch}`
77
+ puts `git merge #{from_branch}`
78
+ raise 'merge failed' if $?.exitstatus != 0
79
+ end
80
+
81
+ def push(branch)
82
+ CommandLine.line_break
83
+ puts "Pushing your changes to branch '#{branch}'"
84
+ puts `git push origin #{branch}`
85
+ end
86
+
87
+
88
+ def checkout(branch)
89
+ puts `git checkout #{branch}`
90
+ end
91
+
92
+ def pull
93
+ puts `git pull`
94
+ end
95
+
96
+ def branch_exist?(branch)
97
+ branches.find { |e| /#{branch}/ =~ e }
98
+ end
99
+
100
+ def new_issue(title, description)
101
+ `ghi -o "#{title}" -m "#{description}"`
102
+ end
103
+
104
+ def branch_has_commits?(branch)
105
+ log = `git log --branches --not --remotes --simplify-by-decoration --decorate --oneline`
106
+ if log.include? branch
107
+ return true
108
+ else
109
+ return false
110
+ end
111
+ end
112
+
113
+ def view_issue(github_issue)
114
+ issue_url = `ghi -u#{github_issue}`
115
+ `open #{issue_url}`
116
+ end
117
+
118
+ def issue(id)
119
+ `ghi -l #{id}`
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ require 'active_resource'
3
+
4
+ module RedmineApi
5
+ class Client < ActiveResource::Base; end
6
+ class Issue < RedmineApi::Client; end
7
+ end
8
+
9
+ module Stagecoach
10
+ class Redmine
11
+ def self.issue(issue_number)
12
+ return RedmineApi::Issue.find(issue_number)
13
+ end
14
+
15
+ def self.issue_url(issue)
16
+ RedmineApi::Client.site + "/issues/" + issue.id
17
+ end
18
+
19
+ # Open the issue in a browser.
20
+ def self.view_issue(issue)
21
+ issue_url = Redmine.issue_url(issue)
22
+ print "Open planio issue in browser? [Y]es or anything else to exit: "
23
+ `open #{issue_url.to_s}` if gets.chomp == "Y"
24
+ puts "Staging completed! Exiting..."
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,78 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "redmine_stagecoach"
8
+ s.version = "0.5.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Oli Barnett"]
12
+ s.date = "2012-01-24"
13
+ s.description = "Git/capistrano workflow automation script with Redmine & Github issue integration"
14
+ s.email = "o.barnett@digitaleseiten.de"
15
+ s.executables = ["stagecoach"]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE.txt",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ ".rvmrc",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "LICENSE.txt",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "bin/stagecoach",
30
+ "lib/.DS_Store",
31
+ "lib/githooks/commit-msg",
32
+ "lib/stagecoach.rb",
33
+ "lib/stagecoach/capistrano.rb",
34
+ "lib/stagecoach/command_line.rb",
35
+ "lib/stagecoach/config.rb",
36
+ "lib/stagecoach/git.rb",
37
+ "lib/stagecoach/redmine.rb",
38
+ "redmine_stagecoach.gemspec",
39
+ "test/helper.rb",
40
+ "test/test_stagecoach.rb"
41
+ ]
42
+ s.homepage = "http://github.com/omnikron/stagecoach"
43
+ s.licenses = ["MIT"]
44
+ s.require_paths = ["lib"]
45
+ s.rubygems_version = "1.8.15"
46
+ s.summary = "Stagecoach is in ur Redmine, automating ur Git workflow."
47
+
48
+ if s.respond_to? :specification_version then
49
+ s.specification_version = 3
50
+
51
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
52
+ s.add_runtime_dependency(%q<ghi>, [">= 0"])
53
+ s.add_runtime_dependency(%q<trollop>, [">= 0"])
54
+ s.add_runtime_dependency(%q<capistrano>, [">= 0"])
55
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
56
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
57
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
58
+ s.add_development_dependency(%q<rcov>, [">= 0"])
59
+ else
60
+ s.add_dependency(%q<ghi>, [">= 0"])
61
+ s.add_dependency(%q<trollop>, [">= 0"])
62
+ s.add_dependency(%q<capistrano>, [">= 0"])
63
+ s.add_dependency(%q<shoulda>, [">= 0"])
64
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
65
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
66
+ s.add_dependency(%q<rcov>, [">= 0"])
67
+ end
68
+ else
69
+ s.add_dependency(%q<ghi>, [">= 0"])
70
+ s.add_dependency(%q<trollop>, [">= 0"])
71
+ s.add_dependency(%q<capistrano>, [">= 0"])
72
+ s.add_dependency(%q<shoulda>, [">= 0"])
73
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
74
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
75
+ s.add_dependency(%q<rcov>, [">= 0"])
76
+ end
77
+ end
78
+
data/test/helper.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'stagecoach'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestStagecoach < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redmine_stagecoach
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Oli Barnett
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-24 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ghi
16
+ requirement: &70166234091840 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70166234091840
25
+ - !ruby/object:Gem::Dependency
26
+ name: trollop
27
+ requirement: &70166234091360 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70166234091360
36
+ - !ruby/object:Gem::Dependency
37
+ name: capistrano
38
+ requirement: &70166234090880 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70166234090880
47
+ - !ruby/object:Gem::Dependency
48
+ name: shoulda
49
+ requirement: &70166234090400 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70166234090400
58
+ - !ruby/object:Gem::Dependency
59
+ name: bundler
60
+ requirement: &70166234089920 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 1.0.0
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70166234089920
69
+ - !ruby/object:Gem::Dependency
70
+ name: jeweler
71
+ requirement: &70166234089440 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 1.6.4
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70166234089440
80
+ - !ruby/object:Gem::Dependency
81
+ name: rcov
82
+ requirement: &70166234088960 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *70166234088960
91
+ description: Git/capistrano workflow automation script with Redmine & Github issue
92
+ integration
93
+ email: o.barnett@digitaleseiten.de
94
+ executables:
95
+ - stagecoach
96
+ extensions: []
97
+ extra_rdoc_files:
98
+ - LICENSE.txt
99
+ - README.rdoc
100
+ files:
101
+ - .document
102
+ - .rvmrc
103
+ - Gemfile
104
+ - Gemfile.lock
105
+ - LICENSE.txt
106
+ - README.rdoc
107
+ - Rakefile
108
+ - VERSION
109
+ - bin/stagecoach
110
+ - lib/.DS_Store
111
+ - lib/githooks/commit-msg
112
+ - lib/stagecoach.rb
113
+ - lib/stagecoach/capistrano.rb
114
+ - lib/stagecoach/command_line.rb
115
+ - lib/stagecoach/config.rb
116
+ - lib/stagecoach/git.rb
117
+ - lib/stagecoach/redmine.rb
118
+ - redmine_stagecoach.gemspec
119
+ - test/helper.rb
120
+ - test/test_stagecoach.rb
121
+ homepage: http://github.com/omnikron/stagecoach
122
+ licenses:
123
+ - MIT
124
+ post_install_message:
125
+ rdoc_options: []
126
+ require_paths:
127
+ - lib
128
+ required_ruby_version: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ segments:
135
+ - 0
136
+ hash: 2500404004806684746
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ! '>='
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubyforge_project:
145
+ rubygems_version: 1.8.15
146
+ signing_key:
147
+ specification_version: 3
148
+ summary: Stagecoach is in ur Redmine, automating ur Git workflow.
149
+ test_files: []