tick

  1. tick
    1. Description
    2. Author
    3. Repository
    4. Requirements
    5. Getting started
    6. Cheat sheet
      1. Read operations
      2. Write operations
    7. Command line API
    8. Query expressions
    9. Format placeholders
    10. Configuration examples
      1. Named listing to list my tickets
      2. Named query to list only the most recently modified tickets
      3. Running git on the tick database
      4. Submitting ticket changes via email
      5. Converting tickets to HTML
      6. Exporting the tick database to other formats
      7. Wrapping tick commands with tick commands
    11. Scheme API
      1. Ticket record accessors
      2. read-ticket
      3. read-ticket-body
      4. write-ticket
      5. hash-ticket
      6. get-username
      7. shorten-hash
      8. define-command
      9. named-listings
      10. define-named-listing
      11. named-sorters
      12. define-named-sorter
      13. named-queries
      14. define-named-query
      15. get-last-used-ticket-hash
      16. set-last-used-ticket-hash!
      17. db-ticket-ids
      18. db-add-ticket
      19. list-ticket-attachments
      20. add-ticket-attachments
      21. delete-ticket-attachments
      22. valid-components
      23. valid-difficulties
      24. valid-milestones
      25. valid-statuses
      26. valid-resolutions
      27. valid-versions
      28. valid-priorities
      29. valid-users
      30. valid-types
    12. History
    13. License
    14. Version history
      1. 0.0.1 (2026-08-08)

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

https://code.call-cc.org/tick

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

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.

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:

Bound variables and ticket accessors:

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.

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:

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)