TicGit-ng 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,77 @@
1
+ module TicGitNG
2
+ module Command
3
+ COMMANDS = {}
4
+ DOC = {}
5
+
6
+ def self.register(mod_name, doc, *commands)
7
+ autoload(mod_name, "ticgit-ng/command/#{mod_name.downcase}")
8
+ DOC[commands] = doc
9
+ commands.each{|cmd| COMMANDS[cmd] = mod_name }
10
+ end
11
+
12
+ register 'Assign', 'Assings a ticket to someone', 'assign'
13
+ register 'Attach', 'Attach file to ticket', 'attach'
14
+ register 'Checkout', 'Checkout a ticket', 'checkout', 'co'
15
+ register 'Comment', 'Comment on a ticket', 'comment'
16
+ register 'List', 'List tickets', 'list'
17
+ register 'Milestone', 'List and modify milestones', 'milestone'
18
+ register 'New', 'Create a new ticket', 'new'
19
+ register 'Points', 'Assign points to a ticket', 'points'
20
+ register 'Recent', 'List recent activities', 'recent'
21
+ register 'Show', 'Show a ticket', 'show'
22
+ register 'State', 'Change state of a ticket', 'state'
23
+ register 'Tag', 'Modify tags of a ticket', 'tag'
24
+ register 'Sync', 'Sync tickets', 'sync'
25
+
26
+ def self.get(command)
27
+ if mod_name = COMMANDS[command]
28
+ const_get(mod_name)
29
+ end
30
+ end
31
+
32
+ def self.usage(action, args)
33
+ option_parser = parser(action, &method(:default_usage))
34
+ option_parser.parse!(args)
35
+ option_parser
36
+ end
37
+
38
+ def self.default_usage(o)
39
+ o.banner = "Usage: ti COMMAND [FLAGS] [ARGS]"
40
+ o.top.append ' ', nil, nil
41
+ o.top.append 'The available ticgit commands are:', nil, nil
42
+
43
+ DOC.each do |commands, doc|
44
+ # get the longest version
45
+ command = commands.sort_by{|cmd| cmd.size }.last
46
+ o.top.append(" %-32s %s" % [command, doc], nil, nil)
47
+ end
48
+ end
49
+
50
+ def self.parser(action, &block)
51
+ OptionParser.new do |o|
52
+ o.banner = "Usage: ti #{action} [FLAGS] [ARGS]"
53
+
54
+ o.base.append ' ', nil, nil
55
+ o.base.append 'Common options:', nil, nil
56
+ o.on_tail('-v', '--version', 'Show the version number'){
57
+ puts TicGitNG::VERSION
58
+ exit
59
+ }
60
+ o.on_tail('-h', '--help', 'Display this help'){
61
+ puts o
62
+ exit
63
+ }
64
+
65
+ if block_given?
66
+ yield(o) if block_given?
67
+ unless o.top.list.empty?
68
+ if action
69
+ o.top.prepend "Options for #{action} command:", nil, nil
70
+ o.top.prepend ' ', nil, nil
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,17 @@
1
+ module TicGitNG
2
+ class Comment
3
+
4
+ attr_reader :base, :user, :added, :comment
5
+
6
+ def initialize(base, file_name, sha)
7
+ @base = base
8
+ @comment = base.git.gblob(sha).contents rescue nil
9
+
10
+ type, date, user = file_name.split('_')
11
+
12
+ @added = Time.at(date.to_i)
13
+ @user = user
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,233 @@
1
+ module TicGitNG
2
+ class Ticket
3
+
4
+ attr_reader :base, :opts
5
+ attr_accessor :ticket_id, :ticket_name
6
+ attr_accessor :title, :state, :milestone, :assigned, :opened, :points
7
+ attr_accessor :comments, :tags, :attachments # arrays
8
+
9
+ def initialize(base, options = {})
10
+ options[:user_name] ||= base.git.config('user.name')
11
+ options[:user_email] ||= base.git.config('user.email')
12
+
13
+ @base = base
14
+ @opts = options || {}
15
+
16
+ @state = 'open' # by default
17
+ @comments = []
18
+ @tags = []
19
+ @attachments = []
20
+ end
21
+
22
+ def self.create(base, title, options = {})
23
+ t = Ticket.new(base, options)
24
+ t.title = title
25
+ t.ticket_name = self.create_ticket_name(title)
26
+ t.save_new
27
+ t
28
+ end
29
+
30
+ def self.open(base, ticket_name, ticket_hash, options = {})
31
+ tid = nil
32
+
33
+ t = Ticket.new(base, options)
34
+ t.ticket_name = ticket_name
35
+
36
+ title, date = self.parse_ticket_name(ticket_name)
37
+ t.opened = date
38
+
39
+ ticket_hash['files'].each do |fname, value|
40
+ if fname == 'TICKET_ID'
41
+ tid = value
42
+ elsif fname == 'TICKET_TITLE'
43
+ t.title = base.git.gblob(value).contents
44
+ else
45
+ # matching
46
+ data = fname.split('_')
47
+
48
+ case data[0]
49
+ when 'ASSIGNED'
50
+ t.assigned = data[1]
51
+ when 'ATTACHMENT'
52
+ t.attachments << TicGitNG::Attachment.new(base, fname, value)
53
+ when 'COMMENT'
54
+ t.comments << TicGitNG::Comment.new(base, fname, value)
55
+ when 'POINTS'
56
+ t.points = base.git.gblob(value).contents.to_i
57
+ when 'STATE'
58
+ t.state = data[1]
59
+ when 'TAG'
60
+ t.tags << data[1]
61
+ when 'TITLE'
62
+ t.title = base.git.gblob(value).contents
63
+ end
64
+ end
65
+ end
66
+
67
+ t.ticket_id = tid
68
+ t
69
+ end
70
+
71
+
72
+ def self.parse_ticket_name(name)
73
+ epoch, title, rand = name.split('_')
74
+ title = title.gsub('-', ' ')
75
+ return [title, Time.at(epoch.to_i)]
76
+ end
77
+
78
+ # write this ticket to the git database
79
+ def save_new
80
+ base.in_branch do |wd|
81
+ base.logger.info "saving #{ticket_name}"
82
+
83
+ Dir.mkdir(ticket_name)
84
+ Dir.chdir(ticket_name) do
85
+ base.new_file('TICKET_ID', ticket_name)
86
+ base.new_file('TICKET_TITLE', title)
87
+ base.new_file('ASSIGNED_' + email, email)
88
+ base.new_file('STATE_' + state, state)
89
+ base.new_file('TITLE', title)
90
+
91
+ # add initial comment
92
+ #COMMENT_080315060503045__schacon_at_gmail
93
+ base.new_file(comment_name(email), opts[:comment]) if opts[:comment]
94
+
95
+ # add initial tags
96
+ if opts[:tags] && opts[:tags].size > 0
97
+ opts[:tags] = opts[:tags].map { |t| t.strip }.compact
98
+ opts[:tags].each do |tag|
99
+ if tag.size > 0
100
+ tag_filename = 'TAG_' + Ticket.clean_string(tag)
101
+ if !File.exists?(tag_filename)
102
+ base.new_file(tag_filename, tag_filename)
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ base.git.add
109
+ base.git.commit("added ticket #{ticket_name}")
110
+ end
111
+ # ticket_id
112
+ end
113
+
114
+ def self.clean_string(string)
115
+ string.downcase.gsub(/[^a-z0-9]+/i, '-')
116
+ end
117
+
118
+ def add_comment(comment)
119
+ return false if !comment
120
+ base.in_branch do |wd|
121
+ Dir.chdir(ticket_name) do
122
+ base.new_file(comment_name(email), comment)
123
+ end
124
+ base.git.add
125
+ base.git.commit("added comment to ticket #{ticket_name}")
126
+ end
127
+ end
128
+
129
+ def change_state(new_state)
130
+ return false if !new_state
131
+ return false if new_state == state
132
+
133
+ base.in_branch do |wd|
134
+ Dir.chdir(ticket_name) do
135
+ base.new_file('STATE_' + new_state, new_state)
136
+ end
137
+ base.git.remove(File.join(ticket_name,'STATE_' + state))
138
+ base.git.add
139
+ base.git.commit("added state (#{new_state}) to ticket #{ticket_name}")
140
+ end
141
+ end
142
+
143
+ def change_assigned(new_assigned)
144
+ new_assigned ||= email
145
+ old_assigned= assigned || ''
146
+ return false if new_assigned == old_assigned
147
+
148
+ base.in_branch do |wd|
149
+ Dir.chdir(ticket_name) do
150
+ base.new_file('ASSIGNED_' + new_assigned, new_assigned)
151
+ end
152
+ base.git.remove(File.join(ticket_name,'ASSIGNED_' + old_assigned))
153
+ base.git.add
154
+ base.git.commit("assigned #{new_assigned} to ticket #{ticket_name}")
155
+ end
156
+ end
157
+
158
+ def change_points(new_points)
159
+ return false if new_points == points
160
+
161
+ base.in_branch do |wd|
162
+ Dir.chdir(ticket_name) do
163
+ base.new_file('POINTS', new_points)
164
+ end
165
+ base.git.add
166
+ base.git.commit("set points to #{new_points} for ticket #{ticket_name}")
167
+ end
168
+ end
169
+
170
+ def add_tag(tag)
171
+ return false if !tag
172
+ added = false
173
+ tags = tag.split(',').map { |t| t.strip }
174
+ base.in_branch do |wd|
175
+ Dir.chdir(ticket_name) do
176
+ tags.each do |add_tag|
177
+ if add_tag.size > 0
178
+ tag_filename = 'TAG_' + Ticket.clean_string(add_tag)
179
+ if !File.exists?(tag_filename)
180
+ base.new_file(tag_filename, tag_filename)
181
+ added = true
182
+ end
183
+ end
184
+ end
185
+ end
186
+ if added
187
+ base.git.add
188
+ base.git.commit("added tags (#{tag}) to ticket #{ticket_name}")
189
+ end
190
+ end
191
+ end
192
+
193
+ def remove_tag(tag)
194
+ return false if !tag
195
+ removed = false
196
+ tags = tag.split(',').map { |t| t.strip }
197
+ base.in_branch do |wd|
198
+ tags.each do |add_tag|
199
+ tag_filename = File.join(ticket_name, 'TAG_' + Ticket.clean_string(add_tag))
200
+ if File.exists?(tag_filename)
201
+ base.git.remove(tag_filename)
202
+ removed = true
203
+ end
204
+ end
205
+ if removed
206
+ base.git.commit("removed tags (#{tag}) from ticket #{ticket_name}")
207
+ end
208
+ end
209
+ end
210
+
211
+ def path
212
+ File.join(state, ticket_name)
213
+ end
214
+
215
+ def comment_name(email)
216
+ 'COMMENT_' + Time.now.to_i.to_s + '_' + email
217
+ end
218
+
219
+ def email
220
+ opts[:user_email] || 'anon'
221
+ end
222
+
223
+ def assigned_name
224
+ assigned.split('@').first rescue ''
225
+ end
226
+
227
+ def self.create_ticket_name(title)
228
+ [Time.now.to_i.to_s, Ticket.clean_string(title), rand(999).to_i.to_s].join('_')
229
+ end
230
+
231
+
232
+ end
233
+ end
@@ -0,0 +1,3 @@
1
+ module TicGitNG
2
+ VERSION = '1.0.0'
3
+ end
data/lib/ticgit-ng.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'fileutils'
2
+ require 'logger'
3
+ require 'optparse'
4
+ require 'ostruct'
5
+ require 'set'
6
+ require 'yaml'
7
+
8
+ # Add the directory containing this file to the start of the load path if it
9
+ # isn't there already.
10
+ $:.unshift(File.dirname(__FILE__)) unless
11
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
12
+
13
+ require 'rubygems'
14
+ # requires git >= 1.0.5
15
+ require 'git'
16
+ require 'ticgit-ng/base'
17
+ require 'ticgit-ng/cli'
18
+
19
+ module TicGitNG
20
+ autoload :VERSION, 'ticgit-ng/version'
21
+ autoload :Comment, 'ticgit-ng/comment'
22
+ autoload :Ticket, 'ticgit-ng/ticket'
23
+
24
+ # options
25
+ # :logger => Logger.new(STDOUT)
26
+ def self.open(git_dir, options = {})
27
+ Base.new(git_dir, options)
28
+ end
29
+
30
+ class OpenStruct < ::OpenStruct
31
+ def to_hash
32
+ @table.dup
33
+ end
34
+ end
35
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: TicGit-ng
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Scott Chacon
14
+ - Jeff Welling
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2011-02-15 00:00:00 -05:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: git
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ hash: 3
31
+ segments:
32
+ - 0
33
+ version: "0"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ type: :development
49
+ version_requirements: *id002
50
+ description: TicGit-ng is a simple ticketing system, roughly similar to the Lighthouse model, that is based in git.
51
+ email:
52
+ - Jeff.Welling@gmail.com
53
+ executables:
54
+ - ti
55
+ - ticgitweb
56
+ extensions: []
57
+
58
+ extra_rdoc_files: []
59
+
60
+ files:
61
+ - bin/ti
62
+ - bin/ticgitweb
63
+ - lib/ticgit-ng.rb
64
+ - lib/ticgit-ng/base.rb
65
+ - lib/ticgit-ng/ticket.rb
66
+ - lib/ticgit-ng/cli.rb
67
+ - lib/ticgit-ng/version.rb
68
+ - lib/ticgit-ng/command/assign.rb
69
+ - lib/ticgit-ng/command/state.rb
70
+ - lib/ticgit-ng/command/new.rb
71
+ - lib/ticgit-ng/command/recent.rb
72
+ - lib/ticgit-ng/command/sync.rb
73
+ - lib/ticgit-ng/command/show.rb
74
+ - lib/ticgit-ng/command/points.rb
75
+ - lib/ticgit-ng/command/milestone.rb
76
+ - lib/ticgit-ng/command/tag.rb
77
+ - lib/ticgit-ng/command/comment.rb
78
+ - lib/ticgit-ng/command/list.rb
79
+ - lib/ticgit-ng/command/checkout.rb
80
+ - lib/ticgit-ng/command.rb
81
+ - lib/ticgit-ng/comment.rb
82
+ - LICENSE_MIT
83
+ - LICENSE_GPL
84
+ - README.mkd
85
+ has_rdoc: true
86
+ homepage: https://github.com/jeffWelling/ticgit
87
+ licenses: []
88
+
89
+ post_install_message:
90
+ rdoc_options: []
91
+
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ hash: 3
100
+ segments:
101
+ - 0
102
+ version: "0"
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ hash: 3
109
+ segments:
110
+ - 0
111
+ version: "0"
112
+ requirements: []
113
+
114
+ rubyforge_project: ticgit-ng
115
+ rubygems_version: 1.3.7
116
+ signing_key:
117
+ specification_version: 3
118
+ summary: Git based distributed ticketing system
119
+ test_files: []
120
+