I built this app to tease out ClojureScript's scripting story while building a useful application.
Specifically, if you're going to write small apps in CLJS+Node.js - YOU NEED TO USE PROMISES! (See Google Closure's Result lib)
Promises let you escape from callback hell, handle real exceptions, structure your code like you normally would in Clojure, and program with values.
It seems that the moment an engineering team becomes constrained, all out-of-band tools and processes get dropped.
One of those processes, ticket tracking, conveys a lot of information about the team and project, and serves as a fundamental mode of async communication. Ironically, one of the best times to collect metrics and keep track of everything is during "crunch-time."
ttt
is an exploration into the idea that our tools should work automatically (in-band), based on the context and data exhaust that exists within our daily operations.
Not all of the functionality is present - namely releases, resolve
, and I'm re-writing search/query with core.logic
This code will most likely not run for you in its current state. It is built against my own custom version of the ClojureScript code base and Closure Lib.
However, the single ttt-example
file should run for you. You can also see it running in the intro screencast
A simple issue tracking system built into the standard git workflow with git hooks. All extra functionality happens with plugins.
It's a git-backed text-based ticket tracker - git ttt
To open a ticket
git ttt -m "This is something we need to do" --type=work --release=0.1 --points=5
git ttt -m "Here's the description of another tictet" -t work -r 0.1 -p 5
To contribute to a ticket
git commit -m "Getting closer to solving the first thing [:1]"
To view a ticket's current info
git ttt :1
To update a ticket
git ttt :1 -r 0.2
To create a sub (or referencing) ticket
git ttt -m "One more ticket" --sub=1 -t work -r 0.1 -p 5
git ttt :3 -s2 # Now this is a sub-ticket for ticket #2
To close a ticket
git commit -m "Fixed the first thing [closed :1]"
git commit -m "Fixed the other thing [closed :2 :3]"
To see an overview of your tickets
git ttt
To see an overview of all your releases
git ttt st
git ttt status
To see ALL tickets
git ttt st -a
To search for tickets
git ttt 'release-0.[1-2]' # Find any ticket that has anything matching the regular expression string
git ttt '[?x :where [?x :owner "paul"]]'
git ttt '[?x :owner "paul"]'
git ttt '[?x :point #"[0-2]H"]' # High impact tickets with 0-2 effort
Get selective - Select the id, type, and points of every open ticket where I'm the owner
git ttt '[(?x :id :type :points) :where [?x :owner "[email protected]" :current-state :open]]'
- When a release's tickets are all closed, a tag is created. This can be toggled off in the configuration file.
- Ticket types are configurable in a file, along with the default type
- Point scales are configurable in a file, along with the default
- Point scales can be any list. For example Fibonacci effort and project impact together
[0L 0M 0H 1L 1M ... 8L 8M 8H]
- Points internally are captured as unicode strings
- Ticket states are configurable in a file
- The config file is in the repo, in EDN format
.ttt/tttrc
- Tickets with contributed work are toggled "in-process" (or whatever state follows open)
- Tickets can be moved into new states using commit messages:
Example commit message [custom-state :5]
- Tickets can have commits/work attributed to them, just listing them in the commit message:
Example commit message [:6 :7]
- Tickets do not have to belong to a release
- Tickets that reference a release will result in an error being thrown
- Tickets are owned by the last person who touches them (unless a pluging changes that behavior)
- Everything in the data structure is timestamped with Unix Time // UTC
- Releases are in the data structure (an EDN file,
tickets.edn
) - Releases are created with tickets via the
--release*=
or-RR
option - Releases can be merged or made into "subprojects" if their
:name
points to another release keyword - Releases are deleted by editing the data file directly; If a ticket is closed and its release is not there, it is ignored.
- Releases should be treated like epics
- All other functionality is added by plugins (the base system is built upon the same foundation as plugins)
- Plugins are stored in
.ttt/plugins/
and toggled in the config file.ttt/.tttrc
- Plugins are free to register new commands/options
- Install involves referencing the ttt script in your ~/.gitconfig
- Alternatively,
ttt
script can be copied to a repo and used directly (without thegit ...
prefix) - ttt will modify your repo's githooks as needed
- ttt is initialized with the first use of the
git ttt
command - ttt can be removed from a repo with
git ttt destroy
{
:releases {
:r0.1 {:name "0.1", :created TIMESTAMP, :completed nil}
:r0.1-mistake {:name :r0.1 :created TIMESTAMP, :completed nil
}
:tickets [
{:id 3
:summary "One more ticket"
:description ""
:reported-by "email from .gitconfig"
:owner "email from .gitconfig"
:closed-by "email from .gitconfig"
:points "5"
:type "work"
:states {:open TIMESTAMP
:in-progress TIMESTAMP
:closed TIMESTAMP
}
:current-state :open
:history [
{ :commit "git hash"
:at TIMESTAMP
},
]
:sub 2
:branch "branch name of the last in-progress commit"
:release "0.1"
}
]
}
ttt needs node
to be installed.
To install for ALL git projects, save the ttt
file somewhere (preferably on your path) and then:
git config --global --add alias.root '!pwd -P'
git config --global --add alias.ttt '!/full/path/to/ttt'
And set your local post-commit git hook to:
git ttt __core__ process-git `git log -n1 --pretty=format:"%H %ae %s"`
To test ttt on a single project, just add the root alias, check the ttt
file into your repo, and call it directly:
`./ttt -m "Making a new ticket"`
And set your local post-commit git hook to:
`git root`/ttt __core__ process-git `git log -n1 --pretty=format:"%H %ae %s"`
A more advanced post-commit hook can be found in this project. It will automerge and maintain the ticket file for you.
TODO
TODO
TODO - just adds a new map to the data structure {:searches { "email from .gitconfig" {:tag "query", ...}}}
TODO - should generate Markdown and HTML
TODO
Searching is powered by core.logic.
If you commit early and often, there will rarely be conflicts. ttt encourages you to do the right thing and helps reinforce best practices.
If a conflict does happen, just git ttt resolve
- this will reorder the conflicting tickets based on timestamps and update all the ticket numbers.
You're also free to fix conflicts by hand (which just involves renumbering the tickets), submit a patch to make resolve
better, or write a plugin.
The logical keys for map-based tickets would be :1, :2, :3
- but if you're numerically indexing something, you're better off just using a vector.
Internally, tickets know their own position within the list via the :id
key.
A lock file is used to ensure that only one edit can happen at a time. When the lock file is present, ttt quits and notifies the user.
If the lock file is mistakenly there, you can just rm .tttlock
Keywords cannot start with an integer. Rather than introduce a project prefix to all tickets, the current choice is to make all IDs integers. In the future, this may change (perhaps via a plugin to introduce project-level controls), but all calling semantics will be the same.
Quick notes: The plugins can be written in any language, but need to read in from stdin and push to stdout.
ttt itself is the public API: ttt __core__ function-name
will return the output of the function
Copyright © 2012 Paul deGrandis
Distributed under the Eclipse Public License, the same as Clojure.
Please see the LICENSE_epl.html
for details.