A really simple personal bug tracker, using just the Unix shell

When working on my personal projects, I find that I often get overwhelmed with ideas and possibilities, causing me to lose track of what needs to be done first, what is important, etc. Some kind of bug tracker would be helpful, except most of them are overkill for my purposes, IMHO. Also, they tend to be platform-dependent (whereas I tend to develop on multiple operating systems) or web-based (and I don't always have internet access). So, I started looking for something Really Simple.

As it turns out, the following is sufficient for my needs:

  • store bugs/ideas as simple text files;
  • use a Unix shell (or similar) to manipulate these files.

Again, this is for *personal* bug tracking. I don't work with other people on these projects. Also, I don't need fancy reports, or a polished user interface. It is meant to be extremely simple.

Anyway, so I thought I'd share this. (Feeble disclaimer: The idea probably isn't new. Hell, maybe it's decades old; if so, I still find it useful.)

This is how it works:

Filenames look like xxxxyz, where xxxx is the number of the item, y is a letter indicating the status (like "o" for open and "c" for closed), and z is a digit indicating the priority. For example, a filename might look like 0007o2, which tells us that this is item #7, it's currently open, and has priority 2.

You add a new item simply by looking at the last number used, and creating a new file with that number plus one, and the desired priority, in your favorite editor. In other words, if I have 27 bugs in my directory, then I create the next one by doing vim 0028o3, or something like that. (I figure you could write a script for this, or maybe it's a one-liner given the right tools... but it seems like overkill when all you need to figure out the next number is ls.)

These files are just text files. They don't have a special format, *except* that the first line should be the title, or a summary of the item. Anything after that has no special meaning. (The reason for this will become clear soon.)

Now, we are ready to unleash the awesome power of the Unix shell onto these files! :-)

Show all open/closed bugs:

$ ls *o*
$ ls *c*

Show a specific bug:

$ cat 0024<Tab><Enter>
# or open it in an editor, of course

Show the number of open bugs:

$ ls -l *o* | wc -l

Show the summaries of all open bugs with priority 1:

$ head -1 *o1

(This is why it's useful to have the title on the first line... head -1 will display just the title. If there are multiple files matching the "*o1" pattern, head will display the names of the files as well.)

Search bugs for the text "refactoring":

$ ack -ui refactoring *
$ grep -i refactoring *

(I use the ack tool, but grep works as well.)

List complete entries for all open bugs with priority 1:

$ head -h 1000 *o1

(Assuming no file has more than a thousand lines. If it does, maybe it's time to split it up...)

Changing status/priority is just a rename:

$ mv 0024o1 0024o2

# or, when files are in svn:
$ svn mv 0024o1 0024o2 --force

Notes:

1. Naturally, you can change the filenames to suit your needs and preferences. I use priorities 1-5, where 1 is the most urgent, but one might well use 1-3 or 1-9, etc. Similarly, there could be more status indicators than just "o" and "c". There could be additional indicators for the type of item (bug, idea, feature), and so on.

2. When I say "Unix shell", I really mean an environment that is similar to sh. Linux and OS X have actual Unix shells; Windows has Cygwin, but in a pinch, 4NT could be used as well (or these days it's Take Command).

:: Comments (2)

I can has scripting?

Brainstorming a bit here...

So I saw this cool one-liner script. It displays a directory tree:

ls -R | grep ":$" | sed -e 's/:$//' -e 's/[^-][^\/]*\//--/g' -e 's/^/   /' -e 's/-/|/'

I have been hacking (on and off) on an Awk-like tool that uses Chicken for scripting. Its working title is s1, and so far it supports begin/main/end blocks like Awk, and $N-notation for quick access to fields.

The real problem is not, adding Awk-like functionality as Scheme functions, but rather, making the Scheme concise enough that it can be used for one-liners. For example, the following works, but is still too verbose:

ls -l | csi -ss s1.scm '(before (set! total 0)) (set! total (+ total (as-number $5))) (after (print total))'

(It shows the total size of files in the current directory, in case it wasn't obvious. :-)

It gets even worse when we try to emulate the ls/grep/sed script mentioned above. Writing this as a one-line just isn't feasible at this point; rather, I have to use a script:

;; tree.s1
;; Call with: ls -aR | csi -ss s1.scm -s tree.s1

(define (branch parts)
  (let* ((parts (reverse parts))
         (dirname (string-trim-right (first parts) #\:))
         (line (apply conc (map (lambda (s) "--") (cdr parts)))))
    (conc "|" line dirname)))

(when (string-suffix? ":" $0)
  (let* ((parts (string-split $0 "/")))
    (print "   " (branch parts))))

Much of the work goes into processing strings and lists, but even without that the code probably still would not fit on one line.

Granted, it's hard to beat the conciseness of regular expressions (as used by grep and sed), but if I want s1 to be useful, it needs to support (much) shorter code. (Maybe I should rewrite it in Arc? ;-)

Here are some thoughts about making this shorter:

1. I dearly miss slicing. In Python, I would have used s[:-1] to chop off the trailing colon. Adding a slice function to the toolbox would probably be useful.

2. I could leverage some of the Awk-like features. This Reddit comment does just that:

find "${1-.}" -type d | awk -F/ '{printf "  |%*s%s\n",(NF-1)*2,"",$NF}'

It takes advantage of the following features:

  • using "/" as a field separator (effectively splitting the path with no extra code)
  • using NF (indicating the number of fields in a line) to compute the indentation level
  • using $NF to display the last field (i.e. the directory name we want to see)

As it happens, s1 can do all these things as well, but it won't be quite as concise. This is something to work on. Maybe there could be a special variable (or symbol) that translates to (length *fields*). I would have to hack the current $-literal to accept any expression, not just a number literal.

The tricky part is to make things concise while still keeping the spirit of Scheme. :-/ s1 will never automagically coerce numbers to strings (or vice versa), for example. I can, however, make it easy to convert them explicitly. (This is also the Pythoneer in me speaking... explicit is better than implicit, and all that good stuff.)

Anyway, there was really no point to this post, except letting the world know that I am tinkering with a new toy. :-) If it ever actually becomes useful, I'll release it... Until then, I might talk more about it here.

:: Comments (3)