tick
- tick
- Description
- Author
- Repository
- Requirements
- Getting started
- Cheat sheet
- Command line API
- Query expressions
- Format placeholders
- Configuration examples
- Scheme API
- Ticket record accessors
- read-ticket
- read-ticket-body
- write-ticket
- hash-ticket
- get-username
- shorten-hash
- define-command
- named-listings
- define-named-listing
- named-sorters
- define-named-sorter
- named-queries
- define-named-query
- get-last-used-ticket-hash
- set-last-used-ticket-hash!
- db-ticket-ids
- db-add-ticket
- list-ticket-attachments
- add-ticket-attachments
- delete-ticket-attachments
- valid-components
- valid-difficulties
- valid-milestones
- valid-statuses
- valid-resolutions
- valid-versions
- valid-priorities
- valid-users
- valid-types
- History
- License
- Version history
Description
tick is a bug tracker which uses git as storage backend.
tick has been designed around the needs of CHICKEN, and the properties of tickets have been modeled after Trac, the bug tracker previously used by CHICKEN.
At the moment, tick's database (i.e., the git repository where tickets are stored), is hosted at the call-cc.org server. Writing to the database requires SSH access to the server, which is only granted to CHICKEN maintainers.
tick can still be used in read-only mode for users without SSH access to call-cc.org, or can be used to submit patches to the repository of tickets, so that people with write access to it can merge the changes. See the Submitting ticket changes via email section for information on how to do that.
Author
Mario Domenech Goulart
Repository
Requirements
tick uses git behind the scenes and expects it to be available on the system.
Eggs:
Getting started
The first time you use tick, run tick init to create the credentials file and clone the repository of tickets.
You'll be prompted by a username. If you had a Trac account before, use the username you used for Trac. If you didn't have a Trac account, pick whatever username you like.
By default, the repository of tickets will be cloned under $XDG_CACHE_HOME/tick/db/ (if $XDG_CACHE_HOME is unset, $HOME/.cache/tick/db/ will be used). Alternatively, the TICK_CACHE_DIR environment variable can be set to make tick place the repository of tickets elsewhere.
Besides the credentials file created by the init command (credentials.conf), tick also uses a configuration file (conf.scm) that can be used to configure parameters (this file is actually loaded by tick, so it can contain arbitrary CHICKEN code).
By default, the directory where tick will look for configuration is $XDG_CONFIG_HOME/tick/ (if $XDG_CONFIG_HOME is unset, $HOME/.config/tick/ will be used). Alternatively, the TICK_CONF_DIR environment variable can be set to make tick read configuration from elsewhere.
Cheat sheet
Below are the commands for the most usual tasks when using tick. Check the help output of the commands for more information about them.
Read operations
- Listing tickets: tick list
- Showing tickets: tick show
- Querying the tickets database: tick query
- Reading ticket properties: tick get
- Updating the repository of tickets: tick pull
- Showing quick statistics about the database: tick info
Write operations
Write operations can be used locally, but at the moment pushing them to the server where the central repository of tickets is hosted requires SSH access.
- Creating tickets: tick create
- Setting ticket properties: tick set
- Adding comments to tickets: tick comment
- Attaching files to tickets: tick attach
- Editing ticket descriptions and comments: tick edit
- Submitting local changes to the server: tick push
Command line API
The command line API of tick is mostly self-documented. For a short list of commands available, run tick list commands.
Each individual command handles help parameters (-h, -help and --help). tick itself does that as well, and will print the help messages of all commands combined.
Query expressions
Query expressions are Scheme expressions which are evaluated in an environment where special operators and variables are available.
The operators (polymorphic -- can be applied to strings, dates and numbers) are:
- =: Equal
- <> or !=: Not equal
- <: Less than
- >: Greater than
- <=: Less than or equal
- >=: Greater than or equal
- ~=: Equal, strings are compared case-insensitively
- ~: True if the second argument matches the regular expression given in the first argument
Bound variables and ticket accessors:
- me: Value of (get-username)
- id: Ticket id (hash)
- trac-id: Ticket Trac id (number, for tickets imported from Trac, or #f for tickets created by tick)
- type: Ticket type
- date: Ticket creation date (YYYY-MM-DD)
- changetime: Ticket last modification time (seconds)
- component: Ticket component
- difficulty: Ticket estimated difficulty
- priority: Ticket priority
- owner: Ticket owner
- reporter: Ticket reporter
- cc: Ticket Cc
- version: Ticket version
- milestone: Ticket milestone
- status: Ticket status
- resolution: Ticket resolution
- summary: Ticket summary
- body: Ticket body
- keywords: Ticket keywords
Examples:
;; Select tickets whose status is not closed for milestone 6.0.0 (and (<> status "closed") (= milestone "6.0.0")) ;; Select tickets owned by me, and which are not closed (and (= owner me) (<> status "closed")) ;; Select tickets created after 2022-11-01 which have "bar" in keywords (and date (> date "2022-11-01") (~ "bar" keywords))
Format placeholders
Format placeholders are special strings that get substituted by the value of ticket object slots. They are typically used as argument to the --format parameter of tick commands.
- {hash}: Ticket id (hash)
- {short-hash}: Ticket short hash
- {trac-id}: Ticket Trac id (for tickets imported from Trac)
- {summary}: Ticket summary
- {changetime}: Ticket last modification time ("%Y-%m-%d %H:%M:%S UTC")
- {datetime}: Ticket creation timestamp ("%Y-%m-%d %H:%M:%S UTC")
- {owner}: Ticket owner
- {reporter}: Ticket reporter
- {status}: Ticket status
- {version}: Ticket version
- {milestone}: Ticket milestone
- {priority}: Ticket priority
- {keywords}: Ticket keywords
- {cc}: Ticket cc
- {type}: Ticket type
- {resolution}: Ticket resolution
Configuration examples
The examples below can be written to the configuration file of tick (typically $HOME/.config/tick/conf.scm).
Named listings, sorters and queries can be combined as necessary to produce outputs in the desired format. They can also be wrapped by custom commands with a terser command line syntax.
Named listing to list my tickets
This example shows how to both filter tickets owned by the user running tick and formatting the output.
(import (chicken format)) (import tick) (define-named-listing 'my-tickets (lambda (ticket) (when (equal? (ticket-owner ticket) (get-username)) (printf "~a ~a~%" (shorten-hash (ticket-id ticket)) (ticket-summary ticket)))))
Usage:
$ tick list -L my-tickets
Named query to list only the most recently modified tickets
This example shows how to query the database for the tickets modified in the last seven days.
(import (chicken time)) (import tick) (define max-latest-news-age ;; Last days in useconds (let ((days 7)) (* (- (current-seconds) (* 3600 24 days)) 1000000))) (define-named-query 'whatsnew `(> changetime ,max-latest-news-age))
Usage:
$ tick query whatsnew
Running git on the tick database
This example shows how to create a git tick command that runs git on the tick database (which is a git repository).
(import (chicken process)) (import commands tick tick-params) (define-command 'git "git <git args> Run git commands on the local database repository" (lambda (args) (process-execute "git" (append (list "-C" (db-dir)) args))))
Usage examples:
$ tick git log $ tick git grep -i "no food jokes"
Submitting ticket changes via email
At the moment write access to the repository of tickets is restricted to CHICKEN maintainers.
You can, however, submit changes that you'd like to make to tickets. Since tick is backed by git, you can submit ticket changes by sending your changes in the format of patches to the CHICKEN maintainers, which can then apply your changes and push them to the repository of tickets.
Below is command that you can execute to generate such patches. It's recommended to run tick pull before generating the patch (or even before creating your changes using tick).
(import (chicken process)) (import commands tick-params) (define-command 'create-patch "create-patch Create patches to be submitted to the chicken-janitors mailing list. The patch will be printed to stdout." (lambda (_) (process-execute "git" (list "-C" (db-dir) "format-patch" "--stdout" "origin/master"))))
Usage:
$ tick pull $ tick create-patch > my-changes.patch
To submit patches, you can either use your favorite email program to send an email with the patch as an attachment to the chicken-janitors mailing list, or use the git send-email program.
$ git send-email my-changes.patch chicken-janitors@nongnu.org
Converting tickets to HTML
Tickets can be converted to HTML by piping the output of tick show to the input of svnwiki2html:
$ tick show HASH | svnwiki2html --css https://wiki.call-cc.org/chicken.css
Exporting the tick database to other formats
This example implements a tick command to convert tickets to JSON and symbolic expressions.
(import (chicken port)) (import commands json tick) (define (tickets->alist) (map (lambda (thash) (let ((ticket (read-ticket thash))) (list->vector (map (lambda (property) (cons property (ticket-ref ticket property))) '(cc changetime component difficulty body id keywords milestone owner priority reporter resolution status summary time trac-id type version))))) (db-ticket-ids))) (define (tickets->json) (with-output-to-string (lambda () (json-write (tickets->alist))))) (define-command 'export "export Export tickets to JSON or sexps" (lambda (args) (if (null? args) (show-command-help 'export 1) (let ((format (car args))) (cond ((equal? format "json") (print (tickets->json))) ((equal? format "sexp") (print (tickets->alist))) (else (die! "Invalid format: ~a" format)))))))
Usage:
$ tick export json $ tick export sexp
Wrapping tick commands with tick commands
If combining named queries, sorters and listings on the command line becomes too awkward to type, you can define reentrant tick commands.
This example shows a rather contrived case where we show tickets owned by the current user first (most recently modified first), limiting the output to 10 tickets and customizing its format.
(import (chicken format) (chicken process) (chicken sort)) (import commands srfi-1 tick) (define-named-listing 'paranoid-listing (let ((count 0)) (lambda (ticket) (when (and (< count 10) (not (equal? (ticket-status ticket) "closed"))) (set! count (add1 count)) (printf "~a owner: ~a, status: ~a, prio ~a, summary: ~a~%" (shorten-hash (ticket-id ticket) 8) (or (ticket-owner ticket) "<nobody>") (ticket-status ticket) (or (ticket-priority ticket) "<not set>") (ticket-summary ticket)))))) (define-named-sorter 'mine-first-and-ordered (lambda (tickets) (append (sort (filter (lambda (ticket) (equal? (ticket-owner ticket) (get-username))) tickets) (lambda (t1 t2) (> (ticket-changetime t1) (ticket-changetime t2)))) (remove (lambda (ticket) (equal? (ticket-owner ticket) (get-username))) tickets)))) (define-command 'yo-dawg "yo-dawg Example of reentrant tick command" (lambda (_) (process-execute "tick" '("list" "-L" "paranoid-listing" "-s" "mine-first-and-ordered"))))
Usage:
$ tick yo-dawg
Scheme API
The Scheme API can be used in the configuration file of tick to extend it.
Ticket record accessors
[procedure] (ticket? TICKET)[procedure] (ticket-body TICKET)
[procedure] (ticket-cc TICKET)
[procedure] (ticket-changetime TICKET)
[procedure] (ticket-component TICKET)
[procedure] (ticket-difficulty TICKET)
[procedure] (ticket-id TICKET)
[procedure] (ticket-keywords TICKET)
[procedure] (ticket-milestone TICKET)
[procedure] (ticket-owner TICKET)
[procedure] (ticket-priority TICKET)
[procedure] (ticket-reporter TICKET)
[procedure] (ticket-resolution TICKET)
[procedure] (ticket-status TICKET)
[procedure] (ticket-summary TICKET)
[procedure] (ticket-time TICKET)
[procedure] (ticket-trac-id TICKET)
[procedure] (ticket-type TICKET)
[procedure] (ticket-version TICKET)
[procedure] (ticket-ref TICKET PROPERTY)
ticket record instances represent individual tickets and their properties. The id slot of ticket objects corresponds to its SHA1 hash.
The trac-id slot is a number for tickets imported from Trac, and #f for tickets created by tick.
read-ticket
<procedure>(read-ticket thash #!key metadata-only?)
Read ticket identified by thash (a string representing the SHA1 hash of the ticket) and return a ticket record instance.
read-ticket-body
[procedure] (read-ticket-body thash)Return the text of the body of ticket identified by thash (a string representing the SHA1 hash of the ticket).
write-ticket
[procedure] (write-ticket ticket)Write ticket (a ticket record instance) to the filesystem, in the database directory (it does not perform git operations) and return its SHA1 hash.
If ticket does not have an id (i.e., (ticket-id ticket) => #f), an identifier (SHA1 hash) will be generated with the hash-ticket procedure.
hash-ticket
[procedure] (hash-ticket ticket)Calculate and return the SHA1 hash of ticket (a ticket record instance). The hash calculation uses the ticket summary, reported and creation time as inputs.
Please note that the hash value for the same inputs might be different when created by CHICKEN 5 and CHICKEN 6, due to differences in the string representation in those CHICKEN versions.
get-username
[procedure] (get-username)Return the username configured in the credentials file.
shorten-hash
[procedure] (shorten-hash hash #!optional (size 6)))Return the first size characters of hash.
define-command
[procedure] (define-command command usage proc)Define a command that can then be used as a tick command on the command line.
command is a symbol naming the command.
usage is a string that will be printed as help message of command, when requested through the command line via any of the help-request options (-h, -help or --help).
proc is a one-argument procedure which will receive the arguments given to the command on the command line.
Example:
(import (chicken string)) (import commands) (define-command 'hello "hello Print `hello' followed the arguments passed to this command" (lambda (args) (print "hello " (string-intersperse args))))
named-listings
[parameter] (named-listings)Alist mapping names (symbols) to one-argument procedures that are given ticket objects.
Named listings allow for the customization of the format in which tickets are listed (e.g., by commands like list and query). Besides formatting, they can also be used as basic filters of tickets.
Named listings are used by the --named-listing parameter of tick commands.
define-named-listing
[procedure] (define-named-listing name proc)Convenient wrapper around the named-listings parameter.
Basically adds an item to the alist yielded by (named-listings), mapping name (symbol) to proc (one-argument procedure that is given a ticket object).
namess defined by define-named-listing can be used as arguments to the --named-listing parameter of tick commands on the command line.
named-sorters
[parameter] (named-sorters)Alist mapping names (symbols) to one-argument procedures that are given a list of ticket objects and are expected to return a list of ticket objects.
Named sorters allow for the customization of the order in which tickets are listed by commands like list and query.
Named sorters are used by the --named-sorter parameter of tick commands.
define-named-sorter
[procedure] (define-named-sorter name proc)Convenient wrapper around the named-sorters parameter.
Basically adds an item to the alist yielded by (named-sorters), mapping name (symbol) to proc (one-argument procedure that is given a list of ticket object and is expected to return a list of ticket objects).
namess defined by define-named-sorter can be used as arguments to the --named-sorter parameter of tick commands on the command line.
named-queries
[parameter] (named-queries)List whose items are 3-element lists of:
- name (symbol)
- query expression (see the Query expressions section)
- listing format (string or #f -- see the Format placeholders section)
Named queries allow for the specification of custom queries, which can then be used as an argument to the tick query command.
define-named-query
[procedure] (define-named-query name query-expr #!key listing-format)Convenient wrapper around the named-queries parameter.
basically adds an item to the list yielded by (named-queries), mapping name (symbol) to query-expr (query expression -- see the "Query expressions" section) and listing-format (string or #f -- see the "Format placeholders" section).
namess defined by define-named-query can be used as arguments to the tick query command on the command line.
get-last-used-ticket-hash
[procedure] (get-last-used-ticket-hash)Return the hash of the ticket that was lastly used by tick commands. That's the procedure used to resolve - when given as argument of tick commands that expect a ticket identifier.
set-last-used-ticket-hash!
[procedure] (set-last-used-ticket-hash! thash)Record thash (string, the ticket hash) as the lastly used hash. The value of - as given as argument to tick commands that expect a ticket identifier is resolved to the value of thash lastly recorded.
db-ticket-ids
[procedure] (db-ticket-ids)Return a list of ticket ids (SHA1 hashes as strings) from the database.
db-add-ticket
[procedure] (db-add-ticket ticket)Add ticket (a ticket record instance) to the database. The ticket is expected to be written to the filesystem (this procedure only performs the git operations to commit the ticket).
list-ticket-attachments
[procedure] (list-ticket-attachments ticket)List attachments linked to ticket ticket (a ticket record instance).
add-ticket-attachments
<procedure>(add-ticket-attachments thash attachments clobber?)<procedure>
Add attachments to the ticket identified by thash (a string representing the SHA1 hash of the ticket). Attachments are a list of paths representing the attachment files that will be linked to the ticket. If clobber? is #t ticket attachments will be clobbered by the ones listed in attachments, in case files of the same name exist.
The ticket area for attachments is flat (i.e., no directories).
This procedure copies files to the ticket area for attachments and adds them to the database (i.e., git commit).
delete-ticket-attachments
[procedure] (delete-ticket-attachments thash attachments)Delete attachment files listed in attachments from the attachment area of the ticket represented by thash (a string representing the SHA1 hash of the ticket).
This procedure will remove files and register the removals in the database (i.e., git commit).
valid-components
[procedure] (valid-components)Return a list of strings representing the valid components as defined in the database.
valid-difficulties
[procedure] (valid-difficulties)Return a list of strings representing the valid estimated difficulties as defined in the database.
valid-milestones
[procedure] (valid-milestones)Return a list of strings representing the valid milestones as defined in the database.
valid-statuses
[procedure] (valid-statuses)Return a list of strings representing the valid statuses as defined in the database.
valid-resolutions
[procedure] (valid-resolutions)Return a list of strings representing the valid resolutions as defined in the database.
valid-versions
[procedure] (valid-versions)Return a list of strings representing the valid versions as defined in the database.
valid-priorities
[procedure] (valid-priorities)Return a list of strings representing the valid priorities as defined in the database.
valid-users
[procedure] (valid-users)Return a list of strings representing the valid users as defined in the database.
valid-types
[procedure] (valid-types)Return a list of strings representing the valid ticket types as defined in the database.
History
For many years CHICKEN used Trac as its bug tracker. Mid 2020's, the LLM mania started and with it rampant abuses from LLM crawlers came. Maintaining the Internet-facing instance of Trac became unmanageable, so the CHICKEN team decided to replace it with something simpler.
The initial idea/wish was to have a simple, low-effort and text-based system. Mario had a barely functional prototype of a program that imported the Trac database into a git repository, where tickets and their metadata were represented by text files. Part of the prototype also included a command line tool that provided an interface to query and manipulate tickets stored in git. Felix, Peter and Mario got together to discuss whether that would be a suitable replacement for Trac. They decided to go ahead with that and the tool eventually became tick.
All Trac history got imported into the tick database. The markup language used by Trac was very loosely and lousily converted to svnwiki syntax. Felix started playing with tick and proposed features, reported a lot of bugs and actively supported the development of tick. Around June 2026, old tickets were being updated and new tickets were being created in tick only. Eventually, the Trac instance was permanently shut down in August 2026.
License
Copyright (c) 2026, Mario Domenech Goulart
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Version history
0.0.1 (2026-08-08)
- Initial release