Thoughts on using the bug tracker effectively. These aren't tips, yet, just something I'm mulling over.
How do we need to deal with issues?
Three modes:
- customer service - very fast response
- triage - fast response (seek info + convergence)
- maintenance - whenever we can
Making it work right
- need use of the bug-tracker to be transparent so that just about anybody can do triage... no complicated policies!
but... we still need some policy, something rigid... something that makes it easier to take a bug and know what to do about it next
- need to distinguish between triaged and non-triaged bugs (but having something like a triaged topic seems like a pain)...
- possible states:
- [need-info] waiting for somebody to tell us something (assigned if we know who)
- [need-volunteer] we know (roughly) what needs to be done, and we need to somebody to do the coding
- [deferred] we're not really going to think about this right now, but feel free to hack on it, of course
- [chatting/assigned] we know what to do and we know who is the best person to do it
- [in-progress] somebody is working on it right now
... http://bugs.darcs.net/issue778 is an example of a bug I don't know how to pigeonhole... we can see that it may be a problem, but we don't know how to reproduce it, nor what to do about it... it's a "huh?", but is it untriaged, then?
- bugs which do not fit into one of these neat categories need to stand out somehow
- main problem is that from the front page, we can't tell the difference between untriaged bugs (our 'huh?' stack) and bugs we know what to do about, but we don't have anybody assigned to yet...
- I wish there was an easy way to tell (precisely) how much people care about a bug. Should be able to count duplicates, at least
Fields
Title
Rough conventions (very informal! the goal is to make things easier to understand at a glance)
'command => specific error message (darcs version)'
- prefix wishes/features with 'wish: '
Priority and Status
Priority:
feature vs. wishlist : these are priorities, so a feature is basically a wish that we really want to have (for example, progress reporting, back in the darcs 1.0 days), whereas a wish is just something that would be nice
Status:
- in-progress : somebody is working on it and if there are patches, they have not made it into the mainline repositories yet
testing: the patch is in, now can you tell us if it does what we want? EricKow thinks we should abandon this status
Tricky:
- duplicate vs. resolved vs. deferred:
- I don't like using resolved for duplicates, because it makes it harder to search for a bug and determine if it has already been fixed or not
- But if we just use 'duplicate' we can't tell if it's a duplicate of a fixed bug or not
- Maybe: deferred for 'this is an interesting duplicate; if the superceder gets resolved, we should get back in touch with the reporter' and duplicate for 'this is a boring duplicate; no need to get back to the superceder'
Topic
Still feel like we're not using the Topics feature in the best way possible
- Default roundup doesn't display topics in a very nice way (I wish they were more like tags)
- Topics we ought to use more consistently:
Questions
- What is the Darcs2 topic for, and is it still meaningful?
Procedures
Some notes on how to do things.
Bulk change of issue status
We wanted to change all issues marked as resolved-in-unstable before a certain date, to resolved. The roundup-admin command-line tool on darcs.net was useful for exploring, but not powerful enough for this task. An interactive python prompt (in an emacs shell for editability) plus the Roundup design document was the solution, once I figured out the api. Here is a simplified transcript. Note I checked the last activity date rather than the date of being marked resolved-in-unstable; the latter was too difficult and turned out to be unnecessary after inspection.
$ python
Python 2.4.4 (#2, Apr 15 2008, 23:43:20)
[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from roundup import instance
>>> t = instance.open('/var/lib/roundup/trackers/darcs')
>>> t
<roundup.instance.Tracker instance at 0xb7d1110c>
>>> db = t.open('admin')
>>> db
<roundpsycopgsql 0x-488ce294>
>>> db.issue
<hyperdb.Class "issue">
>>> db.issue.list()
['145', '202', '259', '354', '448', ...etc
>>> db.issue.count()
1054L
>>> from pprint import pprint as pp
>>> pp(db.issue.getprops())
{'activity': <roundup.hyperdb.Date>,
'actor': <roundup.hyperdb.Link to "user">,
'assignedto': <roundup.hyperdb.Link to "user">,
'creation': <roundup.hyperdb.Date>,
'creator': <roundup.hyperdb.Link to "user">,
'files': <roundup.hyperdb.Multilink to "file">,
'id': <roundup.hyperdb.String>,
'messages': <roundup.hyperdb.Multilink to "msg">,
'nosy': <roundup.hyperdb.Multilink to "user">,
'priority': <roundup.hyperdb.Link to "priority">,
'status': <roundup.hyperdb.Link to "status">,
'superseder': <roundup.hyperdb.Multilink to "issue">,
'title': <roundup.hyperdb.String>,
'topic': <roundup.hyperdb.Multilink to "keyword">}
>>> help(db.issue.get)
Help on method get in module roundup.backends.rdbms_common:
get(self, nodeid, propname, default=[], cache=1) method of roundup.backends.back_postgresql.IssueClass instance
Get the value of a property on an existing node of this class.
'nodeid' must be the id of an existing node of this class or an
IndexError is raised. 'propname' must be the name of a property
of this class or a KeyError is raised.
'cache' exists for backwards compatibility, and is not used.
>>> db.issue.get('792','creation')
<Date 2008-04-12.16:41:47.090>
>>> db.issue.getnode(792)
<roundup.hyperdb.Node instance at 0xb755302c>
>>> db.issue.getnode(792).values()
['7', [], 'Account for --remote-repo in defaultrepo code', ['7', '8', '9', '992'], <Date 2008-04-12.16:41:47.090>, ...
>>> db.issue.getnode(792).keys()
['status', 'topic', 'title', 'nosy', 'creation', 'messages', 'actor', 'priority', 'assignedto', 'creator', 'activity', ...
>>> db.issue.getnode(792).items()
[('status', '7'), ('topic', []), ('title', 'Account for --remote-repo in defaultrepo code'), ('nosy', ['7', '8', '9', ...
>>> db.issue.getnode(792)['status']
'7'
>>> db.issue.getnode(792).status
'7'
>>> db.status
<hyperdb.Class "status">
>>> db.status.list()
['2', '6', '8', '1', '3', '5', '9', '10', '12', '7', '15', '16', '4']
>>> pp([db.status.getnode(id).items() for id in db.status.list()])
[[('name', 'deferred'),
('creator', '1'),
('creation', <Date 2005-10-29.17:28:11.830>),
('actor', '1'),
('order', 2.0),
('activity', <Date 2005-10-29.17:28:11.830>),
('id', '2')],
[('name', 'testing'),
('creator', '1'),
('creation', <Date 2005-10-29.17:28:11.850>),
('actor', '1'),
('order', 6.0),
('activity', <Date 2005-10-29.17:28:11.850>),
('id', '6')],
...
>>> db.issue.find(status='7') # resolved-in-unstable
['145', '200', '22', '207', '580', '136', '510', '209', ...
>>> db.issue.filter(None, {'status':'7'}, [], []) # similar to find
['12', '16', '22', '24', '31', '43', '46', '60', '61', ...
>>> [db.issue.getnode(id).creation for id in db.issue.filter(None, {'status':'7'}, [], [])]
[<Date 2005-11-17.17:30:11.020>, <Date 2005-11-22.12:41:59.780>, ...
>>> from roundup.date import *
>>> Date('2008-06-24')
<Date 2008-06-24.00:00:00.000>
>>> issues = ([db.issue.getnode(id) for id in db.issue.filter(None, {'status':'7'}, [], []) if db.issue.getnode(id).activity < Date('2008-06-24')])
>>> len(issues)
194
>>> issues[0].status
'7'
>>> issues[0].status = '8' # resolved
>>> issues[0].status
'8'
>>> for i in issues: i.status = '8'
...
>>> db.commit()
$
And again, more briefly:
$ python
Python 2.4.4 (#2, Apr 15 2008, 23:43:20)
[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from roundup.date import *
from roundup import instance
db = instance.open('/var/lib/roundup/trackers/darcs').open('admin')
issue = db.issue.getnode
def filterissues(spec={},sort=[],group=[]): return db.issue.filter(None,spec,sort,group)
>>> >>> >>> >>> ... >>>
>>> [i for i in filterissues({'status':'10'}) if issue(i).activity < Date('2008-06-24')] # resolved-in-stable
['37', '65', '69', '113', '127', '141', '143', '147', '173', '186', '218', '259', '264', ...
>>> # manually inspect issues in web interface to see if the above is close enough
>>> [setattr(issue(i),'status','8') for i in filterissues({'status':'10'}) if issue(i).activity < Date('2008-06-24')] #resolved
[None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, ...
>>> db.commit()
>>>
$
Note the commit is done soon after opening the db connection, since I'm not sure how an open db connection interacts with simultaneous changes via the web.
