ditz 0.4 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,42 @@
1
+ Changelog
2
+ INSTALL
3
+ LICENSE
4
+ Manifest.txt
5
+ PLUGINS.txt
6
+ README.txt
7
+ Rakefile
8
+ ReleaseNotes
9
+ bin/ditz
10
+ contrib/completion/_ditz.zsh
11
+ contrib/completion/ditz.bash
12
+ lib/blue-check.png
13
+ lib/component.rhtml
14
+ lib/ditz.rb
15
+ lib/file-storage.rb
16
+ lib/green-bar.png
17
+ lib/green-check.png
18
+ lib/hook.rb
19
+ lib/html.rb
20
+ lib/index.rhtml
21
+ lib/issue.rhtml
22
+ lib/issue_table.rhtml
23
+ lib/lowline.rb
24
+ lib/model-objects.rb
25
+ lib/model.rb
26
+ lib/operator.rb
27
+ lib/plugins/git-sync.rb
28
+ lib/plugins/git.rb
29
+ lib/plugins/issue-claiming.rb
30
+ lib/red-check.png
31
+ lib/release.rhtml
32
+ lib/style.css
33
+ lib/trollop.rb
34
+ lib/unassigned.rhtml
35
+ lib/util.rb
36
+ lib/view.rb
37
+ lib/views.rb
38
+ lib/yellow-bar.png
39
+ man/ditz.1
40
+ setup.rb
41
+ www/index.html
42
+ www/main.css
@@ -0,0 +1,99 @@
1
+ Ditz plugin documentation
2
+
3
+ Shipped plugins:
4
+ 1. git
5
+ 2. git-sync
6
+ 3. issue-claiming
7
+
8
+ git
9
+ ---
10
+
11
+ This plugin allows issues to be associated with git commits and git
12
+ branches. Git commits can be easily tagged with a ditz issue with the 'ditz
13
+ commit' command, and both 'ditz show' and the ditz HTML output will then
14
+ contain a list of associated commits for each issue.
15
+
16
+ Issues can also be assigned a single git feature branch. In this case, all
17
+ commits on that branch will listed as commits for that issue. This
18
+ particular feature is fairly rudimentary, however---it assumes the reference
19
+ point is the 'master' branch, and once the feature branch is merged back
20
+ into master, the list of commits disappears.
21
+
22
+ Two configuration variables are added, which, when specified, are used to
23
+ construct HTML links for the git commit id and branch names in the generated
24
+ HTML output.
25
+
26
+ Commands added:
27
+ ditz set-branch: set the git branch of an issue
28
+ ditz commit: run git-commit, and insert the issue id into the commit
29
+ message.
30
+
31
+ Usage:
32
+ 1. add a line "- git" to the .ditz-plugins file in the project root
33
+ 2. run ditz reconfigure, and enter the URL prefixes, if any, from
34
+ which to create commit and branch links.
35
+ 3. use 'ditz commit' with abandon.
36
+
37
+ git-sync
38
+ --------
39
+
40
+ This plugin is useful for when you want synchronized, non-distributed issue
41
+ coordination with other developers, and you're using git. It allows you to
42
+ synchronize issue updates with other developers by using the 'ditz sync'
43
+ command, which does all the git work of sending and receiving issue change
44
+ for you. However, you have to set things up in a very specific way for this
45
+ to work:
46
+
47
+ 1. Your ditz state must be on a separate branch. I recommend calling it
48
+ 'bugs'. Create this branch, do a ditz init, and push it to the remote
49
+ repo. (This means you won't be able to mingle issue change and code
50
+ change in the same commits. If you care.)
51
+ 2. Make a checkout of the bugs branch in a separate directory, but NOT in
52
+ your code checkout. If you're developing in a directory called "project",
53
+ I recommend making a ../project-bugs/ directory, cloning the repo there
54
+ as well, and keeping that directory checked out to the 'bugs' branch.
55
+ (There are various complicated things you can do to make that directory
56
+ share git objects with your code directory, but I wouldn't bother unless
57
+ you really care about disk space. Just make it an independent clone.)
58
+ 3. Set that directory as your issue-dir in your .ditz-config file in your
59
+ code checkout directory. (This file should be in .gitignore, btw.)
60
+ 4. Run 'ditz reconfigure' and fill in the local branch name, remote
61
+ branch name, and remote repo for the issue tracking branch.
62
+
63
+ Once that's set up, 'ditz sync' will change to the bugs checkout dir, bundle
64
+ up any changes you've made to issue status, push them to the remote repo,
65
+ and pull any new changes in too. All ditz commands will read from your bugs
66
+ directory, so you should be able to use ditz without caring about where
67
+ things are anymore.
68
+
69
+ This complicated setup is necessary to avoid accidentally mingling code
70
+ change and issue change. With this setup, issue change is synchronized,
71
+ but how you synchronize code is still up to you.
72
+
73
+ Usage:
74
+ 0. read all the above text very carefully
75
+ 1. add a line "- git-sync" to the .ditz-plugins file in the project
76
+ root
77
+ 2. run 'ditz reconfigure' and answer its questions
78
+ 3. run 'ditz sync' with abandon
79
+
80
+ issue-claiming
81
+ --------------
82
+
83
+ This plugin allows people to claim issues. This is useful for avoiding
84
+ duplication of work---you can check to see if someone's claimed an
85
+ issue before starting to work on it, and you can let people know what
86
+ you're working on.
87
+
88
+ Commands added:
89
+ ditz claim: claim an issue for yourself
90
+ ditz unclaim: unclaim a claimed issue
91
+ ditz mine: show all issues claimed by you
92
+ ditz claimed: show all claimed issues, by developer
93
+ ditz unclaimed: show all unclaimed issues
94
+
95
+ Usage:
96
+ 1. add a line "- issue-claiming" to the .ditz-plugins file in the project
97
+ root
98
+ 2. use the above commands to abandon
99
+
data/README.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  == ditz
2
2
 
3
- by William Morgan <wmorgan-ditz@masanjin.net>
3
+ by William Morgan <wmorgan-ditz at the masanjin dot nets>
4
4
 
5
5
  http://ditz.rubyforge.org
6
6
 
@@ -14,23 +14,39 @@ Ditz maintains an issue database directory on disk, with files written in a
14
14
  line-based and human-editable format. This directory can be kept under version
15
15
  control, alongside project code.
16
16
 
17
- There are different ways to use ditz:
17
+ Ditz provides a simple, console-based interface for creating and updating the
18
+ issue database files, and some basic static HTML generation capabilities for
19
+ producing world-readable status pages (for a demo, see the ditz ditz page).
18
20
 
19
- 1. Treat issue change the same as code change: include it as part of commits,
20
- and merge it with changes from other developers. (Resolving conflicts in
21
- the usual manner.)
22
- 2. Keep the issue database in the repository but in a separate branch. Issue
23
- changes can be managed by your VCS, but is not tied directly to commits.
24
- 3. Keep the issue database separate and not under VCS at all.
21
+ Ditz includes a robust plugin system for adding commands, model fields, and
22
+ modifying output. See PLUGINS.txt for documentation on the pre-shipped plugins.
25
23
 
26
- Your particular usage will depend on what you want to get out of ditz.
24
+ Ditz currently offers no central public method of bug submission.
27
25
 
28
- Ditz provides a simple, console-based interface for creating and updating the
29
- issue database file, and some rudimentary HTML generation capabilities for
30
- producing world-readable status pages. It currently offers no central public
31
- method of bug submission.
26
+ == USING DITZ
27
+
28
+ There are several different ways to use Ditz:
29
+
30
+ 1. Treat issue change the same as code change: include it as part of commits,
31
+ and merge it with changes from other developers, resolving conflicts in the
32
+ usual manner.
33
+ 2. Keep the issue database in the repository but in a separate branch. Issue
34
+ changes can be managed by your VCS, but is not tied directly to code
35
+ commits.
36
+ 3. Keep the issue database separate and not under VCS at all.
37
+
38
+ All of these options are supported; the choice of which to use depends on your
39
+ workflow.
32
40
 
33
- == SYNOPSIS
41
+ Option #1 is probably most appropriate for the unsynchronized, distributed
42
+ development, since it allows individual developers to modify issue state with a
43
+ minimum of hassle. Option #2 is most suitable for synchronized development, as
44
+ issue state change can be transmitted independently of code change (see also
45
+ the git-sync plugin) and can act as a sychronization mechanism. Option #3 is
46
+ only useful with some other distribution mechanism, like a central web
47
+ interface.
48
+
49
+ == COMMANDLINE SYNOPSIS
34
50
 
35
51
  # set up project. creates the bugs.yaml file.
36
52
  1. ditz init
@@ -54,16 +70,17 @@ method of bug submission.
54
70
 
55
71
  == THE DITZ DATA MODEL
56
72
 
57
- Ditz includes the bare minimum set of features necessary for open-source
58
- development. Features like time spent, priority, assignment of tasks to
59
- developers, due dates, etc. are purposely excluded.
73
+ By default, Ditz includes the bare minimum set of features necessary for
74
+ open-source development. Features like time spent, priority, assignment of
75
+ tasks to developers, due dates, etc. are purposely relegated to the plugin
76
+ system.
60
77
 
61
- A ditz project consists of issues, releases and components.
78
+ A Ditz project consists of issues, releases and components.
62
79
 
63
80
  Issues:
64
- Issues are the fundamental currency of issue tracking. A ditz issue is either
65
- a feature or a bug, but this distinction doesn't affect anything other than
66
- how they're displayed.
81
+ Issues are the fundamental currency of issue tracking. A Ditz issue is either
82
+ a feature or a bug, but this distinction currently doesn't affect anything
83
+ other than how they're displayed.
67
84
 
68
85
  Each issue belongs to exactly one component, and is part of zero or one
69
86
  releases.
@@ -73,9 +90,13 @@ Issues:
73
90
  developers, present and future. Issue ids are typically not exposed to the
74
91
  user.
75
92
 
76
- Issues also have a non-exportable name, which is short and human-readable.
77
- All ditz commands use issue names instead of issue ids. Issue ids may change
78
- in certain circumstances, specifically after a "ditz drop" command.
93
+ Issues also have a non-global, non-exportable name, which is short and
94
+ human-readable. All Ditz commands use issue names in addition to issue ids.
95
+ Issue names (but not issue ids) may change in certain circumstances, e.g.
96
+ after a "ditz drop" command.
97
+
98
+ Issue names can be specified in comments, titles and descriptions, and Ditz
99
+ will automatically rewrite them as necessary when they change.
79
100
 
80
101
  Components:
81
102
  There is always one "general" component, named after the project itself. In
@@ -83,20 +104,14 @@ Components:
83
104
  with the question of which component to assign an issue to.
84
105
 
85
106
  Components simply provide a way of organizing issues, and have no real
86
- functionality. Issues are assigned names derived form the component they're
87
- assigned to.
107
+ functionality. Issues names are derived from the component they're assigned
108
+ to.
88
109
 
89
110
  Releases:
90
111
  A release is the primary grouping mechanism for issues. Status commands like
91
112
  "ditz status" and "ditz todo" always group issues by release. When a release
92
- is 100% complete, it can be marked as released, in which case the associated
93
- issues will cease appearing in ditz status and todo messages.
94
-
95
- == FUTURE WORK
96
-
97
- In future releases, Ditz will have a plugin architecture to allow tighter
98
- integration with specific SCMs and developer communication channels. (See
99
- http://ditz.rubyforge.org/ditz/issue-0704dafe4aef96279364013aba177a0971d425cb.html)
113
+ is 100% complete, it can be marked as released, and its issues will cease
114
+ appearing in Ditz status and todo messages.
100
115
 
101
116
  == LEARNING MORE
102
117
 
@@ -105,7 +120,7 @@ http://ditz.rubyforge.org/ditz/issue-0704dafe4aef96279364013aba177a0971d425cb.ht
105
120
 
106
121
  == REQUIREMENTS
107
122
 
108
- * trollop >= 1.8.2
123
+ * trollop >= 1.8.2, if running via RubyGems.
109
124
 
110
125
  == INSTALLATION
111
126
 
data/Rakefile CHANGED
@@ -17,10 +17,10 @@ Hoe.new('ditz', Ditz::VERSION) do |p|
17
17
  p.url = "http://ditz.rubyforge.org"
18
18
  p.changes = p.paragraphs_of('Changelog', 0..0).join("\n\n")
19
19
  p.email = "wmorgan-ditz@masanjin.net"
20
- p.extra_deps = [['trollop', '>= 1.8.2']]
20
+ p.extra_deps = [['trollop', '>= 1.9']]
21
21
  end
22
22
 
23
- WWW_FILES = FileList["www/*"] + %w(README.txt)
23
+ WWW_FILES = FileList["www/*"] + %w(README.txt PLUGINS.txt)
24
24
 
25
25
  task :upload_webpage => WWW_FILES do |t|
26
26
  sh "rsync -essh -cavz #{t.prerequisites * ' '} wmorgan@rubyforge.org:/var/www/gforge-projects/ditz/"
@@ -31,4 +31,36 @@ task :upload_report do |t|
31
31
  sh "rsync -essh -cavz ditz wmorgan@rubyforge.org:/var/www/gforge-projects/ditz/"
32
32
  end
33
33
 
34
+ task :plugins do |t|
35
+ sh "ruby -w ./make-plugins.txt.rb > PLUGINS.txt"
36
+ end
37
+
38
+ task :really_check_manifest do |t|
39
+ f1 = Tempfile.new "manifest"; f1.close
40
+ f2 = Tempfile.new "manifest"; f2.close
41
+ sh "git ls-files | egrep -v \"^bugs/\" | sort > #{f1.path}"
42
+ sh "sort Manifest.txt > #{f2.path}"
43
+
44
+ f3 = Tempfile.new "manifest"; f3.close
45
+ sh "diff -u #{f1.path} #{f2.path} > #{f3.path}; /bin/true"
46
+
47
+ left, right = [], []
48
+ IO.foreach(f3.path) do |l|
49
+ case l
50
+ when /^\-\-\-/
51
+ when /^\+\+\+/
52
+ when /^\-(.*)\n$/; left << $1
53
+ when /^\+(.*)\n$/; right << $2
54
+ end
55
+ end
56
+
57
+ puts
58
+ puts "Tracked by git but not in Manifest.txt:"
59
+ puts left.empty? ? " <nothing>" : left.map { |l| " " + l }
60
+
61
+ puts
62
+ puts "In Manifest.txt, but not tracked by git:"
63
+ puts right.empty? ? " <nothing>" : right.map { |l| " " + l }
64
+ end
65
+
34
66
  # vim: syntax=ruby
@@ -1,3 +1,9 @@
1
+ 0.5
2
+ ---
3
+
4
+ Two new plugins: git-sync, for synchronized git usage, and issue-claiming,
5
+ which allows you to claim issues for yourself. See PLUGINS.txt for details.
6
+
1
7
  0.4
2
8
  ---
3
9
  - Command-line completion scripts are now included for bash and zsh. To
data/bin/ditz CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby
1
+ #!/usr/bin/ruby1.8
2
2
 
3
3
  ## requires are split in two for efficiency reasons: ditz should be really
4
4
  ## fast when using it for completion.
@@ -11,13 +11,16 @@ if ARGV.include? '--commands'
11
11
  exit 0
12
12
  end
13
13
 
14
- require 'rubygems'
14
+ begin
15
+ require 'rubygems'
16
+ rescue LoadError
17
+ end
18
+
15
19
  require 'fileutils'
16
20
  require 'pathname'
17
21
  require 'trollop'; include Trollop
18
22
  require "ditz"
19
23
 
20
- PROJECT_FN = "project.yaml"
21
24
  CONFIG_FN = ".ditz-config"
22
25
  PLUGIN_FN = ".ditz-plugins"
23
26
 
@@ -30,9 +33,10 @@ $opts = options do
30
33
  opt :config_file, "Configuration file", :default => File.join(config_dir || ".", CONFIG_FN)
31
34
  opt :plugins_file, "Plugins file", :default => File.join(plugin_dir || ".", PLUGIN_FN)
32
35
  opt :verbose, "Verbose output", :default => false
33
- opt :no_comment, "Skip asking for a comment", :default => false
34
36
  opt :list_hooks, "List all hooks and descriptions, and quit.", :short => 'l', :default => false
37
+ stop_on_unknown
35
38
  end
39
+ $verbose = true if $opts[:verbose]
36
40
 
37
41
  Ditz::HookManager.register :startup, <<EOS
38
42
  Executes at startup
@@ -73,18 +77,10 @@ if $opts[:list_hooks]
73
77
  exit 0
74
78
  end
75
79
 
76
- plugins = begin
77
- Ditz::debug "loading plugins from #{$opts[:plugins_file]}"
78
- YAML::load_file$opts[:plugins_file]
80
+ begin
81
+ Ditz::load_plugins $opts[:plugins_file]
79
82
  rescue SystemCallError => e
80
83
  Ditz::debug "can't load plugins file: #{e.message}"
81
- []
82
- end
83
-
84
- plugins.each do |p|
85
- fn = Ditz::find_ditz_file "plugins/#{p}.rb"
86
- Ditz::debug "loading plugin #{p.inspect} from #{fn}"
87
- load fn
88
84
  end
89
85
 
90
86
  config = begin
@@ -105,14 +101,17 @@ EOS
105
101
  end
106
102
  end
107
103
 
108
- cmd = ARGV.shift || "todo"
109
104
  issue_dir = Pathname.new(config.issue_dir || $opts[:issue_dir])
105
+ cmd = ARGV.shift || "todo"
106
+ unless op.has_operation? cmd
107
+ die "no such command: #{cmd}"
108
+ end
110
109
 
111
110
  case cmd # some special commands not handled by Ditz::Operator
112
111
  when "init"
113
112
  die "#{issue_dir} directory already exists" if issue_dir.exist?
114
113
  issue_dir.mkdir
115
- fn = issue_dir + PROJECT_FN
114
+ fn = issue_dir + Ditz::FileStorage::PROJECT_FN
116
115
  project = op.init
117
116
  project.save! fn
118
117
  puts "Ok, #{issue_dir} directory created successfully."
@@ -122,89 +121,67 @@ when "help"
122
121
  exit
123
122
  end
124
123
 
125
- project_root = Ditz::find_dir_containing(issue_dir + PROJECT_FN)
126
- die "No #{issue_dir} directory---use 'ditz init' to initialize" unless project_root
127
- project_root += issue_dir
124
+ $project_root = Ditz::find_dir_containing(issue_dir + Ditz::FileStorage::PROJECT_FN)
125
+ die "No #{issue_dir} directory---use 'ditz init' to initialize" unless $project_root
126
+ $project_root += issue_dir
128
127
 
128
+ storage = Ditz::FileStorage.new $project_root
129
129
  project = begin
130
- fn = project_root + PROJECT_FN
131
- Ditz::debug "loading project from #{fn}"
132
- project = Ditz::Project.from fn
133
-
134
- fn = project_root + "issue-*.yaml"
135
- Ditz::debug "loading issues from #{fn}"
136
- project.issues = Dir[fn].map { |fn| Ditz::Issue.from fn }
137
- Ditz::debug "found #{project.issues.size} issues"
138
- project
130
+ storage.load
139
131
  rescue SystemCallError, Ditz::Project::Error => e
140
132
  die "#{e.message} (use 'init' to initialize)"
141
133
  end
142
134
 
143
- project.validate!
144
- project.issues.each { |p| p.project = project}
145
- project.assign_issue_names!
146
-
147
- unless op.has_operation? cmd
148
- die "no such command: #{cmd}"
149
- end
150
-
151
135
  Ditz::HookManager.run :startup, project, config
152
136
 
153
- ## talk about the law of unintended consequences. 'gets' requires this.
154
- args = []
155
- args << ARGV.shift until ARGV.empty?
156
-
157
137
  Ditz::debug "executing command #{cmd}"
158
138
  begin
159
- op.do cmd, project, config, args
160
- rescue Ditz::Operator::Error => e
161
- die e.message
139
+ op.do cmd, project, config, ARGV
140
+ rescue Ditz::Operator::Error, Ditz::Release::Error, Ditz::Project::Error, Ditz::Issue::Error => e
141
+ $stderr.puts "Error: #{e.message}"
142
+ exit(-1)
162
143
  rescue Errno::EPIPE, Interrupt
163
144
  puts
164
145
  exit 1
165
146
  end
166
147
 
167
- ## save project.yaml
168
- dirty = project.each_modelobject { |o| break true if o.changed? } || false
169
- if dirty
170
- fn = project_root + PROJECT_FN
171
- Ditz::debug "project is dirty, saving #{fn}"
172
- project.save! fn
173
- end
174
-
175
- ## project issues are not model fields proper, so they must be
176
- ## saved independently.
177
148
  changed_issues = project.issues.select { |i| i.changed? }
178
- changed_issues.each do |i|
179
- i.pathname ||= (project_root + "issue-#{i.id}.yaml")
180
- i.project ||= project # hack: not set on new issues
181
- Ditz::debug "issue #{i.name} is dirty, saving #{i.pathname}"
182
- i.save! i.pathname
183
- end
149
+ changed_not_added_issues = changed_issues - project.added_issues
184
150
 
185
- project.deleted_issues.each do |i|
186
- fn = i.pathname
187
- Ditz::debug "issue #{i.name} has been deleted, deleting #{fn}"
188
- FileUtils.rm fn
189
- end
151
+ storage.save project
152
+
153
+ ## at this point, for compatibility with older hook stuff, we set the pathname
154
+ ## directly on the issues.
190
155
 
156
+ project.issues.each { |i| i.pathname = storage.filename_for_issue(i) }
191
157
  unless project.added_issues.empty?
192
158
  unless Ditz::HookManager.run :after_add, project, config, project.added_issues
193
159
  puts "You may have to inform your SCM that the following files have been added:"
194
- project.added_issues.each { |i| puts " " + i.pathname }
160
+ project.added_issues.each { |i| puts " " + storage.filename_for_issue(i) }
195
161
  end
196
162
  end
197
163
 
198
164
  unless project.deleted_issues.empty?
199
165
  unless Ditz::HookManager.run :after_delete, project, config, project.deleted_issues
200
166
  puts "You may have to inform your SCM that the following files have been deleted:"
201
- project.deleted_issues.each { |i| puts " " + i.pathname }
167
+ project.deleted_issues.each { |i| puts " " + storage.filename_for_issue(i) }
202
168
  end
203
169
  end
204
170
 
205
- changed_not_added_issues = changed_issues - project.added_issues
206
171
  unless changed_not_added_issues.empty?
207
- Ditz::HookManager.run :after_update, project, config, changed_not_added_issues
172
+ unless Ditz::HookManager.run :after_update, project, config, changed_not_added_issues
173
+ puts "You may have to inform your SCM that the following files have been modified:"
174
+ changed_not_added_issues.each { |i| puts " " + storage.filename_for_issue(i) }
175
+ end
176
+ end
177
+
178
+ ## hack upon a hack
179
+ if project.changed?
180
+ project.pathname = storage.filename_for_project
181
+ unless Ditz::HookManager.run :after_update, project, config, [project]
182
+ puts "You may have to inform your SCM that the following files have been modified:"
183
+ puts " " + storage.filename_for_project
184
+ end
208
185
  end
209
186
 
210
187
  config.save! $opts[:config_file] if config.changed?