werk 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,16 @@
1
+ I Go To Werk
2
+ ============
3
+
4
+ Werk is a wrapper that combines the ability to query and manipulate tickets in
5
+ Trac with the ability to create and manage repository branches in git. Behaviour is defined largely via .werkrc files, which are scanned in order up the filesystem from your current location. This makes it possible to define workflow via .werkrc files located in-project, with sensible per-user definitions for defaults.
6
+
7
+ TODO
8
+ ====
9
+
10
+ - start using trac actions
11
+ - come up with a better way of baking branch / ticket names
12
+ - start using optparse
13
+ - add more workflows to wrap add
14
+ - add documentation for the various commands (possibly generate this from doc?)
15
+ - move definitions out to .werkrc files
16
+ - factor trac and git out from global namespace
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'rspec/core/rake_task'
2
+ desc "Run all tests"
3
+ RSpec::Core::RakeTask.new('spec') do |t|
4
+ t.pattern = 'spec/**/*.rb'
5
+ end
6
+
7
+ task :default => :spec
data/bin/werk ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'werk/cli'
4
+
5
+ Werk::CLI.start
data/lib/werk.rb ADDED
@@ -0,0 +1 @@
1
+ require 'werk/base'
data/lib/werk/base.rb ADDED
@@ -0,0 +1,63 @@
1
+ require 'rubygems'
2
+ require 'yaml'
3
+
4
+ require 'werk/trac'
5
+ require 'werk/git'
6
+ require 'werk/commands'
7
+
8
+ module Werk
9
+ class Base
10
+ include Werk::Trac
11
+ include Werk::Git
12
+ include Werk::Commands
13
+
14
+ def initialize(runtime_options = {})
15
+ if runtime_options[:rcfile] && File.file?(runtime_options[:rcfile])
16
+ # If the user explicitly specified an rcfile, just use that instead
17
+ options = YAML.load_file(File.expand_path(runtime_options[:rcfile]))
18
+ else
19
+ # Go all the way to the root looking for .werkrcs, then apply them in
20
+ # reverse order to ensure local changes trump more global ones
21
+ current = Dir.pwd
22
+ options = {}
23
+
24
+ loop do
25
+ %w( .werkrc ).each do |file|
26
+ if File.file?(file)
27
+ # Do this backwards so we give more nested files precendece
28
+ puts "Loading options from #{File.join(Dir.pwd,file)}" if runtime_options[:verbose]
29
+ options = YAML.load_file(file).merge(options)
30
+ end
31
+ end
32
+ pwd = Dir.pwd
33
+ Dir.chdir("..")
34
+ break if pwd == Dir.pwd # if changing the directory made no difference, then we're at the top
35
+ end
36
+ Dir.chdir(current)
37
+ end
38
+
39
+ # Now read in any explicitly specified arguments
40
+ options.merge!(runtime_options)
41
+ options.each do |option, value|
42
+ self.instance_variable_set "@#{option.to_s}", value
43
+ end
44
+ end
45
+
46
+ def requires_variables(vars)
47
+ vars.each do |option|
48
+ if !self.instance_variable_defined? "@#{option}"
49
+ puts(<<-EOM)
50
+
51
+ You need to specify a value for #{option}. To do so, add a line like
52
+
53
+ #{option}: <value>
54
+
55
+ to your ~/.werkrc file (or any other .werkrc up the directory hierarchy)
56
+
57
+ EOM
58
+ exit
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
data/lib/werk/cli.rb ADDED
@@ -0,0 +1,43 @@
1
+ require 'optparse'
2
+ require 'werk/base'
3
+
4
+ module Werk
5
+ module CLI
6
+ module Options
7
+ def self.order!(args)
8
+ options = {}
9
+ optparse = OptionParser.new do |opts|
10
+ opts.banner = "Usage: werk [options] <command> [arguments]"
11
+
12
+ opts.on( '-f', '--rcfile FILE', 'Load the specified werkrc file (in lieu of .werkrc files)' ) do |file|
13
+ options[:rcfile] = file
14
+ end
15
+
16
+ opts.on( '-v', '--verbose', 'Be verbose' ) do
17
+ options[:verbose] = true
18
+ end
19
+
20
+ opts.on( '-h', '--help', 'Display this screen' ) do
21
+ puts opts
22
+ exit
23
+ end
24
+ end
25
+ begin
26
+ optparse.order!(args)
27
+ raise "You need to specify a command" if args.empty?
28
+ rescue
29
+ puts $!
30
+ puts optparse
31
+ exit
32
+ end
33
+ options
34
+ end
35
+ end
36
+
37
+ def self.start
38
+ inst = Werk::Base.new(Options.order!(ARGV))
39
+ result = inst.send ARGV.shift, *ARGV
40
+ puts result unless result.nil?
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,97 @@
1
+ module Werk
2
+ module Commands
3
+ def list
4
+ ticket_list
5
+ end
6
+
7
+ def show(branch=nil)
8
+ branch = repo_current_head unless branch
9
+
10
+ # This'll throw if we can't get a ticket out
11
+ ticket = branch2ticket(branch)
12
+ system "open #{@trac_url.sub('xmlrpc', 'ticket/' + ticket)}"
13
+ end
14
+
15
+ def start(ticket, *args)
16
+ # Are we starting a hotfix?
17
+ hotfix = args.include? "--hotfix"
18
+
19
+ # Start a ticket named 'ticket', branching from the correct place if needed
20
+ repo_branch(ticket2branch(ticket, hotfix), (hotfix)? "production" : "master")
21
+
22
+ # Push the branch up to origin
23
+ repo_push
24
+
25
+ # Annotate the ticket
26
+ take_action(branch2ticket(ticket), "start")
27
+ end
28
+
29
+ def markfortest(branch = nil)
30
+ take_action(branch, "testing")
31
+ end
32
+
33
+ def markrejected(branch = nil)
34
+ take_action(branch, "reject")
35
+ end
36
+
37
+ def markaccepted(branch = nil)
38
+ take_action(branch, "pass")
39
+ end
40
+
41
+ def mergeandclose(branch = nil)
42
+ branch = repo_current_head unless branch
43
+
44
+
45
+ # This'll throw if we can't get a ticket out
46
+ ticket = branch2ticket(branch)
47
+
48
+ # Push the branch
49
+ repo_checkout(branch)
50
+ repo_pull
51
+ repo_push
52
+
53
+ # Pull a new master / production
54
+ targets = ["master"]
55
+ targets << "production" if branch.include? "hotfix"
56
+ targets.each do |target|
57
+ repo_checkout(target)
58
+ repo_pull
59
+ repo_merge(branch)
60
+ repo_push
61
+ end
62
+
63
+ # Annotate the ticket
64
+ take_action(ticket, "merged")
65
+ end
66
+
67
+ private
68
+ def take_action(branch, action)
69
+ branch = repo_current_head unless branch
70
+
71
+ # This'll throw if we can't get a ticket out
72
+ ticket = branch2ticket(branch)
73
+
74
+ # Push the branch
75
+ repo_push
76
+
77
+ # Annotate the ticket according to the action
78
+ actions = ticket_actions(ticket).map { |x| x[0] }
79
+
80
+ raise "#{action} is not a valid action for ticket #{ticket}. Valid actions are #{actions.join ', '}" unless actions.include? action
81
+ ticket_annotate(ticket, "Changed via werk", :action => action)
82
+
83
+ nil
84
+ end
85
+
86
+ def ticket2branch(name, hotfix = false)
87
+ prefix = (hotfix)? "hotfix" : "ticket"
88
+ "#{prefix}_#{branch2ticket(name)}"
89
+ end
90
+
91
+ def branch2ticket(name)
92
+ result = /\D*(\d*).*/.match(name)[1]
93
+ raise "Not on a ticket branch" if result.nil?
94
+ result
95
+ end
96
+ end
97
+ end
data/lib/werk/git.rb ADDED
@@ -0,0 +1,36 @@
1
+ module Werk
2
+ module Git
3
+ def repo_checkout(new_branch, *args)
4
+ system "git co #{new_branch}"
5
+ end
6
+
7
+ def repo_branch(new_branch, source_branch = "HEAD", *args)
8
+ require_clean
9
+ system "git co -b #{new_branch} #{source_branch}"
10
+ end
11
+
12
+ def repo_merge(from_branch, *args)
13
+ require_clean
14
+ system "git merge --no-ff #{from_branch}"
15
+ end
16
+
17
+ def repo_push(remote = "origin")
18
+ require_clean
19
+ system "git push #{remote} HEAD"
20
+ end
21
+
22
+ def repo_pull(remote = "origin")
23
+ require_clean
24
+ system "git pull #{remote}"
25
+ end
26
+
27
+ def repo_current_head
28
+ `git symbolic-ref HEAD`.gsub("refs/heads/", "")
29
+ end
30
+
31
+ private
32
+ def require_clean
33
+ raise "Your working copy isn't clean" unless system "git diff --quiet"
34
+ end
35
+ end
36
+ end
data/lib/werk/trac.rb ADDED
@@ -0,0 +1,49 @@
1
+ require 'trac4r'
2
+ require 'erb'
3
+
4
+ module Werk
5
+ module Trac
6
+ def ticket_list(*args)
7
+ setup_trac
8
+ conditions = {}
9
+ conditions[:owner] = @trac_user unless args.include? "--everyone"
10
+ conditions[:status] = "!closed" unless args.include? "--all"
11
+ @trac.tickets.query(conditions).each do |ticket_id|
12
+ ticket_show(ticket_id, :oneline)
13
+ end
14
+ end
15
+
16
+ def ticket_show(ticket_id, format = :long)
17
+ setup_trac
18
+ ticket = ticket_get(ticket_id)
19
+ case format
20
+ when :long
21
+ template = File.read(File.join(File.dirname(__FILE__), '..', '..', 'templates', 'ticket_show.erb'))
22
+ ERB.new(template).result(ticket.send :binding)
23
+ when :oneline
24
+ "#{ticket.id}: #{ticket.summary} (#{ticket.status})"
25
+ end
26
+ end
27
+
28
+ def ticket_actions(ticket_id)
29
+ setup_trac
30
+ @trac.tickets.actions(ticket_id)
31
+ end
32
+
33
+ def ticket_annotate(ticket_id, comment, attributes = {})
34
+ setup_trac
35
+ @trac.tickets.update ticket_id.to_i, comment, attributes, false
36
+ end
37
+
38
+ private
39
+ def ticket_get(ticket_id)
40
+ setup_trac
41
+ @trac.tickets.get ticket_id
42
+ end
43
+
44
+ def setup_trac
45
+ requires_variables %w( trac_url trac_user trac_password )
46
+ @trac = ::Trac.new(@trac_url, @trac_user, @trac_password) unless defined? @trac
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,13 @@
1
+ ID: <%= id %>
2
+ Owner: <%= owner %>
3
+ Reported By: <%= reporter %>
4
+ Created: <%= created_at.to_time %>
5
+ Last Updated: <%= updated_at.to_time %>
6
+ Status: <%= status %>
7
+ Summary: <%= summary %>
8
+ Type: <%= type %>
9
+ Priority: <%= priority %>
10
+
11
+ Description:
12
+
13
+ <%= description %>
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: werk
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 3
10
+ version: 0.0.3
11
+ platform: ruby
12
+ authors:
13
+ - Mat Trudel
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-12-30 00:00:00 -05:00
19
+ default_executable: werk
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: trac4r
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ description: "werk combines ticketing and repos into definable workflows. I go to werk. "
36
+ email:
37
+ - mat@geeky.net
38
+ executables:
39
+ - werk
40
+ extensions: []
41
+
42
+ extra_rdoc_files:
43
+ - README.md
44
+ files:
45
+ - README.md
46
+ - Rakefile
47
+ - bin/werk
48
+ - lib/werk.rb
49
+ - lib/werk/base.rb
50
+ - lib/werk/cli.rb
51
+ - lib/werk/commands.rb
52
+ - lib/werk/git.rb
53
+ - lib/werk/trac.rb
54
+ - templates/ticket_show.erb
55
+ has_rdoc: true
56
+ homepage: http://github.com/mtrudel/werk
57
+ licenses: []
58
+
59
+ post_install_message:
60
+ rdoc_options: []
61
+
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ hash: 3
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ hash: 3
79
+ segments:
80
+ - 0
81
+ version: "0"
82
+ requirements: []
83
+
84
+ rubyforge_project:
85
+ rubygems_version: 1.3.7
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: werk is a developer workflow tool that makes teams happy
89
+ test_files: []
90
+