gen-tj 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/README.rdoc +36 -0
- data/Rakefile +29 -0
- data/bin/gen-buglist +40 -0
- data/bin/gen-crosscheck +42 -0
- data/bin/gen-resources +36 -0
- data/bin/gen-tj +44 -0
- data/gen-tj.gemspec +30 -0
- data/lib/gen-tj.rb +1 -0
- data/lib/gen-tj/crosscheck.rb +76 -0
- data/lib/gen-tj/gen-tj.rb +87 -0
- data/lib/gen-tj/genbuglist.rb +63 -0
- data/lib/gen-tj/genresources.rb +80 -0
- data/lib/gen-tj/task.rb +77 -0
- data/lib/gen-tj/version.rb +5 -0
- data/samples/Makefile +27 -0
- data/samples/README.rdoc +26 -0
- data/samples/project.tjp +73 -0
- data/samples/reports.tji +128 -0
- data/samples/resources.yml +26 -0
- metadata +126 -0
data/README.rdoc
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
= gen-tj -- generate taskjuggler plans from FATE
|
2
|
+
|
3
|
+
This generates Taskjuggler (www.taskjugger.org) plans from (carefully
|
4
|
+
designed) FATE entries combined with Bugzilla assignments.
|
5
|
+
|
6
|
+
== Commands
|
7
|
+
=== gen-buglist -- generate taskjuggler plans from Bugzilla
|
8
|
+
|
9
|
+
gen-buglist runs a named query and converts the resulting bug list
|
10
|
+
into tasks
|
11
|
+
|
12
|
+
Usage
|
13
|
+
gen-buglist name-of-query > buglist.tji
|
14
|
+
|
15
|
+
=== gen-resources -- generate TJ resources (incl. vacations)
|
16
|
+
|
17
|
+
gen-resources takes a "resource.yml" file and checks (via
|
18
|
+
present.suse.de) the vacations.
|
19
|
+
|
20
|
+
Usage
|
21
|
+
gen-resources > resources.tji
|
22
|
+
|
23
|
+
=== gen-tj -- generate TJ task list from FATE assignments
|
24
|
+
|
25
|
+
gen-tj generates a TaskJuggler task list (.tji file) from
|
26
|
+
FATE assignments prioritized by a FATE relation tree
|
27
|
+
|
28
|
+
Usage
|
29
|
+
gen-tj relationtree-name tji-file
|
30
|
+
|
31
|
+
|
32
|
+
== Makefile
|
33
|
+
|
34
|
+
Combining the three tasks (resources, buglist, tasklist) can easily be
|
35
|
+
done with a Makefile as provided in the samples directory.
|
36
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
extra_docs = ['README*', 'samples/*' ]
|
5
|
+
|
6
|
+
task :default => [:test]
|
7
|
+
|
8
|
+
Rake::TestTask.new do |t|
|
9
|
+
t.libs << File.expand_path('../test', __FILE__)
|
10
|
+
t.libs << File.expand_path('../', __FILE__)
|
11
|
+
t.test_files = FileList['test/test*.rb']
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
begin
|
16
|
+
require 'yard'
|
17
|
+
YARD::Rake::YardocTask.new(:doc) do |t|
|
18
|
+
t.files = ['lib/**/*.rb', *extra_docs]
|
19
|
+
t.options = ['--no-private']
|
20
|
+
end
|
21
|
+
rescue LoadError
|
22
|
+
STDERR.puts "Install yard if you want prettier docs"
|
23
|
+
require 'rdoc/task'
|
24
|
+
Rake::RDocTask.new(:doc) do |rdoc|
|
25
|
+
rdoc.rdoc_dir = "doc"
|
26
|
+
rdoc.title = "dm-bugzilla-adapter #{DataMapper::Adapters::BugzillaAdapter::VERSION}"
|
27
|
+
extra_docs.each { |ex| rdoc.rdoc_files.include ex }
|
28
|
+
end
|
29
|
+
end
|
data/bin/gen-buglist
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2011 SUSE LINUX Products GmbH
|
4
|
+
#
|
5
|
+
# Author: Klaus Kämpf <kkaempf@suse.com>
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
# a copy of this software and associated documentation files (the
|
9
|
+
# "Software"), to deal in the Software without restriction, including
|
10
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
# the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
#++
|
26
|
+
$:.push(File.join(File.dirname(__FILE__), '..', 'lib'))
|
27
|
+
require 'rubygems'
|
28
|
+
require 'gen-tj/genbuglist'
|
29
|
+
|
30
|
+
def usage msg
|
31
|
+
STDERR.puts "*** Error: #{msg}"
|
32
|
+
STDERR.puts "Usage: #{File.basename(__FILE__)} <query-name>"
|
33
|
+
exit 1
|
34
|
+
end
|
35
|
+
|
36
|
+
query = ARGV.shift
|
37
|
+
|
38
|
+
usage "No query name given" unless query
|
39
|
+
|
40
|
+
GenTJ::GenBuglist.run query
|
data/bin/gen-crosscheck
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2011 SUSE LINUX Products GmbH
|
4
|
+
#
|
5
|
+
# Author: Klaus Kämpf <kkaempf@suse.com>
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
# a copy of this software and associated documentation files (the
|
9
|
+
# "Software"), to deal in the Software without restriction, including
|
10
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
# the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
#++
|
26
|
+
$:.push(File.join(File.dirname(__FILE__), '..', 'lib'))
|
27
|
+
require 'rubygems'
|
28
|
+
require 'gen-tj/crosscheck'
|
29
|
+
|
30
|
+
def usage msg
|
31
|
+
STDERR.puts "*** Error: #{msg}"
|
32
|
+
STDERR.puts "Usage: #{File.basename(__FILE__)} <title>"
|
33
|
+
exit 1
|
34
|
+
end
|
35
|
+
|
36
|
+
title = ARGV.shift
|
37
|
+
|
38
|
+
usage "title missing" unless title
|
39
|
+
|
40
|
+
STDERR.puts "Checking '#{title}'"
|
41
|
+
|
42
|
+
GenTJ::Crosscheck.run title
|
data/bin/gen-resources
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2011 SUSE LINUX Products GmbH
|
4
|
+
#
|
5
|
+
# Author: Klaus Kämpf <kkaempf@suse.com>
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
# a copy of this software and associated documentation files (the
|
9
|
+
# "Software"), to deal in the Software without restriction, including
|
10
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
# the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
#++
|
26
|
+
$:.push(File.join(File.dirname(__FILE__), '..', 'lib'))
|
27
|
+
require 'rubygems'
|
28
|
+
require 'gen-tj/genresources'
|
29
|
+
|
30
|
+
def usage msg
|
31
|
+
STDERR.puts "*** Error: #{msg}"
|
32
|
+
STDERR.puts "Usage: #{File.basename(__FILE__)} [<resources.yml>]"
|
33
|
+
exit 1
|
34
|
+
end
|
35
|
+
|
36
|
+
GenTJ::Genresources.run(ARGV.shift || "resources.yml")
|
data/bin/gen-tj
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2011 SUSE LINUX Products GmbH
|
4
|
+
#
|
5
|
+
# Author: Klaus Kämpf <kkaempf@suse.com>
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
# a copy of this software and associated documentation files (the
|
9
|
+
# "Software"), to deal in the Software without restriction, including
|
10
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
# the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
#++
|
26
|
+
|
27
|
+
def usage msg
|
28
|
+
STDERR.puts "*** Error: #{msg}"
|
29
|
+
STDERR.puts "Usage: #{File.basename(__FILE__)} <treename> <prjname>"
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
|
33
|
+
$:.push(File.join(File.dirname(__FILE__), '..', 'lib'))
|
34
|
+
require 'rubygems'
|
35
|
+
require 'gen-tj'
|
36
|
+
|
37
|
+
treename = ARGV.shift
|
38
|
+
usage "treename missing" unless treename
|
39
|
+
|
40
|
+
prjname = ARGV.shift
|
41
|
+
usage "prjname missing" unless prjname
|
42
|
+
|
43
|
+
GenTJ::GenTJ.main Dir.getwd, treename, prjname
|
44
|
+
|
data/gen-tj.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "gen-tj/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "gen-tj"
|
7
|
+
s.version = GenTJ::GenTJ::VERSION
|
8
|
+
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.authors = ["Klaus Kämpf"]
|
11
|
+
s.email = ["kkaempf@suse.de"]
|
12
|
+
s.homepage = ""
|
13
|
+
s.summary = %q{Generate Taskjuggler plans from Bugzilla and FATE}
|
14
|
+
s.description = %q{Given a list of resources (developers), a
|
15
|
+
Bugzilla named query and a FATE relationtree, this tool generates a
|
16
|
+
TaskJuggler overview of current work assigned to developers.
|
17
|
+
Additionally, it checks present.suse.de for vacations and public
|
18
|
+
holidays.}
|
19
|
+
|
20
|
+
s.add_dependency("dm-keeper-adapter", ["~> 0.0.4"])
|
21
|
+
s.add_dependency("dm-bugzilla-adapter", ["~> 0.0.1"])
|
22
|
+
|
23
|
+
s.rubyforge_project = "gen-tj"
|
24
|
+
|
25
|
+
s.files = `git ls-files`.split("\n")
|
26
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
27
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
28
|
+
s.extra_rdoc_files = `git ls-files -- {samples}/*`.split("\n")
|
29
|
+
s.require_paths = ["lib"]
|
30
|
+
end
|
data/lib/gen-tj.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'gen-tj/gen-tj.rb'
|
@@ -0,0 +1,76 @@
|
|
1
|
+
#
|
2
|
+
# crosscheck.rb
|
3
|
+
#
|
4
|
+
# Crosscheck if features are in relationtree
|
5
|
+
#
|
6
|
+
# Usage: crosscheck "<match>"
|
7
|
+
#
|
8
|
+
# Example: ruby crosscheck.rb "Manager 1.2.1"
|
9
|
+
#
|
10
|
+
# <match> must exist in feature title _and_ name of relation tree
|
11
|
+
#
|
12
|
+
#
|
13
|
+
|
14
|
+
$: << File.join(File.dirname(__FILE__), "..", "dm-keeper-adapter", "lib")
|
15
|
+
|
16
|
+
require 'rubygems'
|
17
|
+
require 'dm-keeper-adapter'
|
18
|
+
require 'nokogiri'
|
19
|
+
|
20
|
+
module GenTJ
|
21
|
+
class Crosscheck
|
22
|
+
def Crosscheck.run title
|
23
|
+
|
24
|
+
DataMapper::Logger.new($stdout, :debug)
|
25
|
+
keeper = DataMapper.setup(:default, :adapter => 'keeper',
|
26
|
+
:url => 'https://keeper.novell.com/sxkeeper')
|
27
|
+
require 'keeper/feature'
|
28
|
+
require 'keeper/relationtree'
|
29
|
+
require 'keeper/relation'
|
30
|
+
DataMapper.finalize
|
31
|
+
|
32
|
+
# Get all features with a matching title
|
33
|
+
|
34
|
+
features = {}
|
35
|
+
Feature.all(:title.like => title).each do |f|
|
36
|
+
features[f.id] = f
|
37
|
+
end
|
38
|
+
|
39
|
+
unless features.size>0
|
40
|
+
STDERR.puts "No features matching '#{title}' found"
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
# Get the corresponding relationtree
|
45
|
+
relationtree = Relationtree.first(:title.like => title)
|
46
|
+
|
47
|
+
unless relationtree
|
48
|
+
STDERR.puts "No relationtree matching '#{title}' found"
|
49
|
+
return
|
50
|
+
end
|
51
|
+
|
52
|
+
# Now iterate through the relationtree and remove matching features
|
53
|
+
|
54
|
+
size_of_relation_tree = 0
|
55
|
+
relationtree.relations.each do |relation|
|
56
|
+
target = relation.target
|
57
|
+
size_of_relation_tree += 1
|
58
|
+
t_id = target.to_i
|
59
|
+
if features.delete(t_id).nil?
|
60
|
+
puts "Target #{t_id} has a bad title"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
if size_of_relation_tree == 0
|
65
|
+
STDERR.puts "Relationtree '#{title}' has no features"
|
66
|
+
return
|
67
|
+
end
|
68
|
+
|
69
|
+
# Print out left over features
|
70
|
+
|
71
|
+
features.each do |id,f|
|
72
|
+
puts "Missing in relation tree - #{id}:'#{f.title}'"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
#
|
2
|
+
# gentj.rb
|
3
|
+
#
|
4
|
+
# Generate TaskJuggler tasks from Fate relation tree
|
5
|
+
#
|
6
|
+
# Usage:
|
7
|
+
# ruby gentj.rb <relation-tree-title> <tji-name>
|
8
|
+
# Example:
|
9
|
+
# ruby gentj.rb "Project tree" project
|
10
|
+
# -> generates 'project.tji' file
|
11
|
+
#
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'dm-bugzilla-adapter'
|
15
|
+
require 'dm-keeper-adapter'
|
16
|
+
require 'nokogiri'
|
17
|
+
require File.join(File.dirname(__FILE__),'task')
|
18
|
+
|
19
|
+
class Task
|
20
|
+
|
21
|
+
def add_relations relations, flags = nil
|
22
|
+
relations.each do |relation|
|
23
|
+
id = relation.target
|
24
|
+
f = Feature.get(id)
|
25
|
+
|
26
|
+
unless f.milestone =~ /^1\.2/
|
27
|
+
STDERR.puts "Skipping feature #{id} with milestone '#{f.milestone}'"
|
28
|
+
next
|
29
|
+
end
|
30
|
+
|
31
|
+
raise "No feature #{id}" unless f
|
32
|
+
prio = relation.sort_position
|
33
|
+
t = Task.new f.title, id
|
34
|
+
case f.developer
|
35
|
+
when /<email.*>/
|
36
|
+
alloc = Nokogiri.XML "<alloc>#{f.developer}</alloc>"
|
37
|
+
alloc.xpath("//email").each do |mail|
|
38
|
+
mail.text =~ /(.*)@(.*)/
|
39
|
+
t.allocations << $1
|
40
|
+
end
|
41
|
+
when /(.*)@(.*)/
|
42
|
+
t.allocations << $1
|
43
|
+
else
|
44
|
+
t.allocations << "dev"
|
45
|
+
end
|
46
|
+
|
47
|
+
if relation.parent
|
48
|
+
t.add_relations relation, :noprio => true
|
49
|
+
end
|
50
|
+
t.priority = prio unless flags && flags[:noprio]
|
51
|
+
|
52
|
+
self.add t
|
53
|
+
end # relations.each
|
54
|
+
end # def
|
55
|
+
|
56
|
+
end # class
|
57
|
+
|
58
|
+
module GenTJ
|
59
|
+
class GenTJ
|
60
|
+
def GenTJ.main dir, treename, subtitle
|
61
|
+
DataMapper::Logger.new($stdout, :debug)
|
62
|
+
keeper = DataMapper.setup(:default, :adapter => 'keeper', :url => 'https://keeper.novell.com/sxkeeper')
|
63
|
+
require 'keeper/feature'
|
64
|
+
require 'keeper/relationtree'
|
65
|
+
require 'keeper/relation'
|
66
|
+
DataMapper.finalize
|
67
|
+
|
68
|
+
# get 'my' relationtree
|
69
|
+
|
70
|
+
relationtree = Relationtree.first(:title => treename)
|
71
|
+
unless relationtree
|
72
|
+
STDERR.puts "No relationtree named '#{treename}' found"
|
73
|
+
return
|
74
|
+
end
|
75
|
+
|
76
|
+
title = relationtree.title
|
77
|
+
|
78
|
+
task = Task.new(title,subtitle)
|
79
|
+
|
80
|
+
task.add_relations(relationtree.relations)
|
81
|
+
|
82
|
+
File.open(File.join(dir,"#{subtitle}.tji"), "w+") do |f|
|
83
|
+
f.puts task.to_tj
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#
|
2
|
+
# named_query2tji.rb
|
3
|
+
#
|
4
|
+
# Convert list of bugs (generated by named query) to TJ include
|
5
|
+
#
|
6
|
+
# Usage:
|
7
|
+
# named_query2tji <name-of-query>
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'rubygems'
|
11
|
+
require 'dm-bugzilla-adapter'
|
12
|
+
|
13
|
+
# extend DataMapper Bug
|
14
|
+
class Bug
|
15
|
+
def to_tj
|
16
|
+
a = @assigned_to.split('@').first.gsub(".","_")
|
17
|
+
s = @summary.gsub("\"","\\\"")
|
18
|
+
s = "task bug_#{@id} \"#{s}\" {\n"
|
19
|
+
s << " allocate #{a}\n"
|
20
|
+
s << " effort 2d\n"
|
21
|
+
s << "}\n"
|
22
|
+
s
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module GenTJ
|
27
|
+
class GenBuglist
|
28
|
+
def GenBuglist.run query
|
29
|
+
DataMapper::Logger.new(STDOUT, :debug)
|
30
|
+
keeper = DataMapper.setup(:default, :adapter => 'bugzilla',
|
31
|
+
:url => 'https://bugzilla.novell.com')
|
32
|
+
require 'bugzilla/named_query'
|
33
|
+
DataMapper.finalize
|
34
|
+
|
35
|
+
f = NamedQuery.get(query)
|
36
|
+
|
37
|
+
bugs = f.bugs
|
38
|
+
|
39
|
+
# sort bugs according to priority
|
40
|
+
|
41
|
+
prios = {}
|
42
|
+
|
43
|
+
bugs.each do |bug|
|
44
|
+
prios[bug.priority] ||= []
|
45
|
+
prios[bug.priority] << bug
|
46
|
+
end
|
47
|
+
|
48
|
+
puts "task bugs \"Bugs\" {"
|
49
|
+
prios.each do |k,v|
|
50
|
+
pval = "_other"
|
51
|
+
if k =~ /P(\d)+.*/
|
52
|
+
pval = $1
|
53
|
+
end
|
54
|
+
puts "task p#{pval}_bugs \"Bugs with prio #{k}\" {"
|
55
|
+
v.each do |bug|
|
56
|
+
puts bug.to_tj
|
57
|
+
end
|
58
|
+
puts "}"
|
59
|
+
end
|
60
|
+
puts "}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#
|
2
|
+
# Generate TJ resources from YAML file + present.suse.de:9874
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'yaml'
|
6
|
+
require 'net/telnet'
|
7
|
+
|
8
|
+
module GenTJ
|
9
|
+
class Genresources
|
10
|
+
|
11
|
+
#
|
12
|
+
# Create vacations entry for 'name', write to 'to'
|
13
|
+
#
|
14
|
+
# Use present.suse.de:9874 to extract dates
|
15
|
+
#
|
16
|
+
def Genresources.create_vacations name, to
|
17
|
+
tn = Net::Telnet.new('Host' => 'present.suse.de', 'Port' => 9874, 'Binmode' => true)
|
18
|
+
collect = false
|
19
|
+
tn.cmd(name) do |data|
|
20
|
+
# STDERR.puts "<#{name}> -> #{data}"
|
21
|
+
data.split("\n").each do |l|
|
22
|
+
if l =~ /^Absence/
|
23
|
+
collect = true
|
24
|
+
end
|
25
|
+
next unless collect
|
26
|
+
if l[0,1] == "-"
|
27
|
+
collect = false
|
28
|
+
next
|
29
|
+
end
|
30
|
+
dates = []
|
31
|
+
l.split(" ").each do |date|
|
32
|
+
next unless date =~ /2011/
|
33
|
+
dates << date
|
34
|
+
end
|
35
|
+
case dates.size
|
36
|
+
when 1: to.puts " vacation #{dates[0]}"
|
37
|
+
when 2: to.puts " vacation #{dates[0]} - #{dates[1]}"
|
38
|
+
else
|
39
|
+
STDERR.puts "#{dates.size} dates for (#{name}) '#{l}'"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
tn.close
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Create taskjuggler 'resource' entry for login, write to 'to'
|
48
|
+
#
|
49
|
+
# 'value' is either a string (name of resource)
|
50
|
+
# or a Hash (denoting a team)
|
51
|
+
#
|
52
|
+
def Genresources.create_tj login, value, to
|
53
|
+
return if login == "_name"
|
54
|
+
name = value.is_a?(String) ? value : value['_name']
|
55
|
+
to.puts "resource #{login} #{name.inspect} {"
|
56
|
+
case value
|
57
|
+
when String: create_vacations name, to
|
58
|
+
when Hash
|
59
|
+
value.each do |k,v|
|
60
|
+
create_tj k, v, to
|
61
|
+
to.puts "flags team"
|
62
|
+
end
|
63
|
+
else
|
64
|
+
raise "Can't handle #{value.class} for #{login}"
|
65
|
+
end
|
66
|
+
to.puts "}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def Genresources.run input, output = nil
|
70
|
+
resources = YAML::load(File.open( input ))
|
71
|
+
|
72
|
+
out = File.open(output) rescue STDOUT
|
73
|
+
|
74
|
+
out.puts "flags team"
|
75
|
+
resources.each do |login, value|
|
76
|
+
create_tj login, value, out
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/gen-tj/task.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
#
|
2
|
+
# Class representing a task (aka Fate feature)
|
3
|
+
#
|
4
|
+
class Task
|
5
|
+
attr_reader :title, :effort, :duration, :status
|
6
|
+
attr_accessor :allocations
|
7
|
+
|
8
|
+
def initialize(title, fate = nil)
|
9
|
+
# title = '[manager , ***] blah blah'
|
10
|
+
if title =~ /\[(.*)\](.*)/
|
11
|
+
# $1 = full match, i.e. '[manager , ***, D] blah blah'
|
12
|
+
# $2 = sub match, i.e 'manager, ***, D'
|
13
|
+
@title = $2.strip
|
14
|
+
if $1 =~ /(.*),(.*)[,(.*)]?/
|
15
|
+
case $2.strip
|
16
|
+
when "*": @effort = "2d"
|
17
|
+
when "**": @effort = "1w"
|
18
|
+
when "***": @effort = "2w"
|
19
|
+
when "****": @effort = "4w"
|
20
|
+
else
|
21
|
+
@effort = $2
|
22
|
+
end
|
23
|
+
if $3 && $3 == "D"
|
24
|
+
@status = :done
|
25
|
+
end
|
26
|
+
end
|
27
|
+
else
|
28
|
+
@title = title
|
29
|
+
end
|
30
|
+
@fate = fate
|
31
|
+
@allocations = []
|
32
|
+
end
|
33
|
+
|
34
|
+
def add task
|
35
|
+
@subtasks ||= []
|
36
|
+
@subtasks << task
|
37
|
+
end
|
38
|
+
|
39
|
+
def priority= prio
|
40
|
+
@priority = prio.to_i
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Convert Task to tj
|
45
|
+
#
|
46
|
+
def to_tj
|
47
|
+
s = "task "
|
48
|
+
if @fate
|
49
|
+
s << "fate_#{@fate}"
|
50
|
+
else
|
51
|
+
s << "task_#{self}"
|
52
|
+
end
|
53
|
+
t = @title.tr("\"", "'")
|
54
|
+
s << " \"#{t}\" {\n"
|
55
|
+
s << " priority #{1000-@priority}\n" if @priority
|
56
|
+
s << " start ${now}\n"
|
57
|
+
if @subtasks
|
58
|
+
@subtasks.each do |task|
|
59
|
+
s << task.to_tj
|
60
|
+
end
|
61
|
+
else
|
62
|
+
if @allocations.size == 1
|
63
|
+
s << " allocate #{allocations[0]}\n"
|
64
|
+
else
|
65
|
+
@allocations.each do |rsrc|
|
66
|
+
s << " allocate #{rsrc} { mandatory }\n"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
s << " duration #{duration}\n" if @duration
|
71
|
+
s << " effort #{effort}\n" if @effort
|
72
|
+
end
|
73
|
+
s << "}\n"
|
74
|
+
|
75
|
+
s
|
76
|
+
end
|
77
|
+
end
|
data/samples/Makefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Name of TaskJuggler binary, change to 'tj' for TaskJuggler 2.x
|
2
|
+
TASKJUGGLER = tj3
|
3
|
+
|
4
|
+
# Name of Relationtree in FATE
|
5
|
+
RELATIONTREE = "Name of Relationtree"
|
6
|
+
|
7
|
+
# Name of query in Bugzilla
|
8
|
+
QUERYNAME = "Name of Bugzilla Query"
|
9
|
+
|
10
|
+
all: project.tjp resources.tji buglist.tji
|
11
|
+
gen-tj $(RELATIONTREE) project
|
12
|
+
$(TASKJUGGLER) $<
|
13
|
+
|
14
|
+
resources.tji: resources.yml
|
15
|
+
gen-resources $< > $@-tmp
|
16
|
+
mv -f $@-tmp $@
|
17
|
+
|
18
|
+
buglist.tji:
|
19
|
+
gen-buglist $(QUERYNAME) > $@-tmp
|
20
|
+
mv -f $@-tmp $@
|
21
|
+
|
22
|
+
clean:
|
23
|
+
rm -f buglist.tji
|
24
|
+
rm -f resources.tji
|
25
|
+
rm -f *.html
|
26
|
+
rm -rf css icons scripts
|
27
|
+
|
data/samples/README.rdoc
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
= Sample files for gen-tj
|
2
|
+
|
3
|
+
This directory contains samples for files _you_ need to provide in
|
4
|
+
order to use gen-tj
|
5
|
+
|
6
|
+
Look into the files for further comments.
|
7
|
+
|
8
|
+
== resources.yml - Allocatable resources for this project
|
9
|
+
|
10
|
+
A YAML (www.yaml.org) formatted file describing your resources
|
11
|
+
(developers, testers, documentation, etc.)
|
12
|
+
|
13
|
+
== project.tjp - Project description
|
14
|
+
|
15
|
+
A TaskJuggler project file describing your project (name, start date,
|
16
|
+
end date, ...), public holidays, extra tasks (i.e. testing), and
|
17
|
+
milestones.
|
18
|
+
|
19
|
+
== reports.tji - Reports to generate
|
20
|
+
|
21
|
+
This file describes which reports to generate. The sample generates
|
22
|
+
HTML formatted
|
23
|
+
* Overview - list of tasks
|
24
|
+
* Status - daily status
|
25
|
+
* Development - A list of tasks showing the resources assigned to each task
|
26
|
+
* Resource - A graph showing resource allocation. It identifies whether each resource is under- or over-allocated for.
|
data/samples/project.tjp
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
project my_project "Name of project" 2011-08-01 - 2015-08-01 {
|
2
|
+
weekstartsmonday
|
3
|
+
timeformat "%a %Y-%m-%d"
|
4
|
+
}
|
5
|
+
|
6
|
+
# german public holidays
|
7
|
+
|
8
|
+
vacation "Christi Himmelfahrt" 2011-06-02
|
9
|
+
vacation "Pfingstmontag" 2011-06-13
|
10
|
+
vacation "Fronleichnam" 2011-06-23
|
11
|
+
vacation "Mariä Himmelfahrt" 2011-08-15
|
12
|
+
vacation "Tag der Deutschen Einheit" 2011-10-03
|
13
|
+
vacation "Allerheiligen" 2011-11-01
|
14
|
+
vacation "2. Weihnachtstag" 2011-12-26
|
15
|
+
vacation "Workshop" 2011-07-11 - 2011-07-15
|
16
|
+
vacation "Hackweek" 2011-09-26 - 2011-09-30
|
17
|
+
|
18
|
+
# Define resources (generated by gen-resources)
|
19
|
+
include "resources.tji"
|
20
|
+
|
21
|
+
# Define FATE tasks (generated by gen-tj)
|
22
|
+
include "project.tji"
|
23
|
+
|
24
|
+
# Define Bug assignments (generated by gen-buglist)
|
25
|
+
|
26
|
+
include "buglist.tji"
|
27
|
+
|
28
|
+
#
|
29
|
+
# Testing tasks
|
30
|
+
#
|
31
|
+
|
32
|
+
task testing "Beta/RC testing" {
|
33
|
+
task beta1_testing "Beta 1 testing" {
|
34
|
+
# Everyone in the 'dev' team will do testing
|
35
|
+
allocate dev
|
36
|
+
duration 2w
|
37
|
+
# Insert a proper dependency here - whats the criteria to start beta testing ?
|
38
|
+
depends !!fate_my_project
|
39
|
+
}
|
40
|
+
task beta2_testing "Beta 2 testing" {
|
41
|
+
allocate dev
|
42
|
+
duration 2w
|
43
|
+
depends !beta1_testing
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
#
|
48
|
+
# Milestones
|
49
|
+
#
|
50
|
+
# Milestones only have dependencies, but no duration/effort and no allocations
|
51
|
+
#
|
52
|
+
|
53
|
+
task milestones "Milestones" {
|
54
|
+
task start "start" {
|
55
|
+
start ${projectstart}
|
56
|
+
}
|
57
|
+
task alpha0 "alpha 0" {
|
58
|
+
# Insert a proper dependency here - whats the criteria to release Alpha0 ?
|
59
|
+
depends !!my_project.feature_for_alpha0
|
60
|
+
}
|
61
|
+
task alpha1 "alpha 1" {
|
62
|
+
# Alpha 1 comes after Alpha 0
|
63
|
+
depends !alpha0
|
64
|
+
# Insert a proper dependency here - whats the criteria to release Alpha1 ?
|
65
|
+
depends !!my_project.feature_for_alpha1
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
#
|
70
|
+
# Description of (HTML) reports to generate
|
71
|
+
#
|
72
|
+
|
73
|
+
include "reports.tji"
|
data/samples/reports.tji
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
#
|
2
|
+
# A sample reports.tji file
|
3
|
+
#
|
4
|
+
#
|
5
|
+
|
6
|
+
navigator navbar {
|
7
|
+
hidereport 0
|
8
|
+
}
|
9
|
+
|
10
|
+
macro TaskTip [
|
11
|
+
tooltip istask() -8<-
|
12
|
+
'''Start: ''' <-query attribute='start'->
|
13
|
+
'''End: ''' <-query attribute='end'->
|
14
|
+
----
|
15
|
+
'''Resources:'''
|
16
|
+
|
17
|
+
<-query attribute='resources'->
|
18
|
+
----
|
19
|
+
'''Precursors: '''
|
20
|
+
|
21
|
+
<-query attribute='precursors'->
|
22
|
+
----
|
23
|
+
'''Followers: '''
|
24
|
+
|
25
|
+
<-query attribute='followers'->
|
26
|
+
->8-
|
27
|
+
]
|
28
|
+
|
29
|
+
macro AlertColor [
|
30
|
+
cellcolor plan.alert = 0 "#00D000" # green
|
31
|
+
cellcolor plan.alert = 1 "#D0D000" # yellow
|
32
|
+
cellcolor plan.alert = 2 "#D00000" # red
|
33
|
+
]
|
34
|
+
|
35
|
+
textreport frame "" {
|
36
|
+
header -8<-
|
37
|
+
== Development Project ==
|
38
|
+
----
|
39
|
+
<[navigator id="navbar"]>
|
40
|
+
----
|
41
|
+
->8-
|
42
|
+
footer "----"
|
43
|
+
textreport index "Overview" {
|
44
|
+
formats html
|
45
|
+
center '<[report id="overview"]>'
|
46
|
+
}
|
47
|
+
|
48
|
+
textreport "Status" {
|
49
|
+
formats html
|
50
|
+
center -8<-
|
51
|
+
<[report id="status.dashboard"]>
|
52
|
+
->8-
|
53
|
+
}
|
54
|
+
|
55
|
+
textreport development "Development" {
|
56
|
+
formats html
|
57
|
+
center '<[report id="development"]>'
|
58
|
+
}
|
59
|
+
|
60
|
+
textreport "ResourceGraph" {
|
61
|
+
formats html
|
62
|
+
title "Resource Graph"
|
63
|
+
center '<[report id="resourceGraph"]>'
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
|
68
|
+
taskreport overview "" {
|
69
|
+
columns hierarchindex, name, start, end, effort, duration,
|
70
|
+
chart { ${TaskTip} },
|
71
|
+
resources { width 150
|
72
|
+
listtype bullets }
|
73
|
+
|
74
|
+
footer -8<-
|
75
|
+
=== Staffing ===
|
76
|
+
|
77
|
+
All project phases are properly staffed. See [[ResourceGraph]] for
|
78
|
+
detailed resource allocations.
|
79
|
+
|
80
|
+
->8-
|
81
|
+
|
82
|
+
}
|
83
|
+
|
84
|
+
taskreport status "" {
|
85
|
+
columns wbs { width 50 }, name { width 150 },
|
86
|
+
start { width 100 }, end { width 100 },
|
87
|
+
effort { width 100 },
|
88
|
+
alert { tooltip plan.journal
|
89
|
+
!= '' "<-query attribute='journal'->" width 150 },
|
90
|
+
status { width 150 }
|
91
|
+
|
92
|
+
taskreport dashboard "" {
|
93
|
+
headline "Project Dashboard (<-query attribute='now'->)"
|
94
|
+
columns name { title "Task" ${AlertColor} width 200},
|
95
|
+
resources { width 200 ${AlertColor}
|
96
|
+
listtype bullets
|
97
|
+
# listitem "<-query attribute='name'->"
|
98
|
+
start ${projectstart} end ${projectend} },
|
99
|
+
alerttrend { title "Trend" ${AlertColor} width 50 },
|
100
|
+
journalmessages { width 350 ${AlertColor} }
|
101
|
+
hidetask ~hasalert(0)
|
102
|
+
# sorttasks alert.down, delayed.end.up
|
103
|
+
period %{${now} - 1w} +1w
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
|
108
|
+
# A list of tasks showing the resources assigned to each task.
|
109
|
+
taskreport development "" {
|
110
|
+
headline "Development - Resource Allocation Report"
|
111
|
+
columns hierarchindex, name, start, end, effort { title "Work" },
|
112
|
+
duration, chart { ${TaskTip} scale day width 500 }
|
113
|
+
timeformat "%Y-%m-%d"
|
114
|
+
hideresource ~(isleaf() & isleaf_())
|
115
|
+
sortresources name.up
|
116
|
+
}
|
117
|
+
|
118
|
+
# A graph showing resource allocation. It identifies whether each
|
119
|
+
# resource is under- or over-allocated for.
|
120
|
+
resourcereport resourceGraph "" {
|
121
|
+
headline "Resource Allocation Graph"
|
122
|
+
columns no, name, effort, rate, weekly { ${TaskTip} }
|
123
|
+
loadunit shortauto
|
124
|
+
# We only like to show leaf tasks for leaf resources.
|
125
|
+
hidetask ~(isleaf() & isleaf_())
|
126
|
+
sorttasks plan.start.up
|
127
|
+
}
|
128
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#
|
2
|
+
# Sample resources.yml file
|
3
|
+
#
|
4
|
+
#
|
5
|
+
# Format: YAML - see yaml.org
|
6
|
+
#
|
7
|
+
# Assumptions
|
8
|
+
#
|
9
|
+
# login names are shared between FATE and Bugzilla
|
10
|
+
#
|
11
|
+
#
|
12
|
+
|
13
|
+
login1: "Real name1"
|
14
|
+
login2: "Real name2"
|
15
|
+
|
16
|
+
# You can structure developers into teams
|
17
|
+
# Start with team nickname (here 'team') and indent
|
18
|
+
# Use the '_name:" attribute to give the team a name
|
19
|
+
|
20
|
+
team:
|
21
|
+
_name: "Name of team"
|
22
|
+
# you can also use teams within teams
|
23
|
+
subteam:
|
24
|
+
_name: "Name of subteam"
|
25
|
+
login3: "A subteam developer"
|
26
|
+
login4: "A team developer"
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gen-tj
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- "Klaus K\xC3\xA4mpf"
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-09-28 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: dm-keeper-adapter
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 23
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 0
|
33
|
+
- 4
|
34
|
+
version: 0.0.4
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: dm-bugzilla-adapter
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 29
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 0
|
49
|
+
- 1
|
50
|
+
version: 0.0.1
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
description: |-
|
54
|
+
Given a list of resources (developers), a
|
55
|
+
Bugzilla named query and a FATE relationtree, this tool generates a
|
56
|
+
TaskJuggler overview of current work assigned to developers.
|
57
|
+
Additionally, it checks present.suse.de for vacations and public
|
58
|
+
holidays.
|
59
|
+
email:
|
60
|
+
- kkaempf@suse.de
|
61
|
+
executables:
|
62
|
+
- gen-buglist
|
63
|
+
- gen-crosscheck
|
64
|
+
- gen-resources
|
65
|
+
- gen-tj
|
66
|
+
extensions: []
|
67
|
+
|
68
|
+
extra_rdoc_files: []
|
69
|
+
|
70
|
+
files:
|
71
|
+
- .gitignore
|
72
|
+
- README.rdoc
|
73
|
+
- Rakefile
|
74
|
+
- bin/gen-buglist
|
75
|
+
- bin/gen-crosscheck
|
76
|
+
- bin/gen-resources
|
77
|
+
- bin/gen-tj
|
78
|
+
- gen-tj.gemspec
|
79
|
+
- lib/gen-tj.rb
|
80
|
+
- lib/gen-tj/crosscheck.rb
|
81
|
+
- lib/gen-tj/gen-tj.rb
|
82
|
+
- lib/gen-tj/genbuglist.rb
|
83
|
+
- lib/gen-tj/genresources.rb
|
84
|
+
- lib/gen-tj/task.rb
|
85
|
+
- lib/gen-tj/version.rb
|
86
|
+
- samples/Makefile
|
87
|
+
- samples/README.rdoc
|
88
|
+
- samples/project.tjp
|
89
|
+
- samples/reports.tji
|
90
|
+
- samples/resources.yml
|
91
|
+
has_rdoc: true
|
92
|
+
homepage: ""
|
93
|
+
licenses: []
|
94
|
+
|
95
|
+
post_install_message:
|
96
|
+
rdoc_options: []
|
97
|
+
|
98
|
+
require_paths:
|
99
|
+
- lib
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
hash: 3
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
version: "0"
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
hash: 3
|
115
|
+
segments:
|
116
|
+
- 0
|
117
|
+
version: "0"
|
118
|
+
requirements: []
|
119
|
+
|
120
|
+
rubyforge_project: gen-tj
|
121
|
+
rubygems_version: 1.5.2
|
122
|
+
signing_key:
|
123
|
+
specification_version: 3
|
124
|
+
summary: Generate Taskjuggler plans from Bugzilla and FATE
|
125
|
+
test_files: []
|
126
|
+
|