Wiki
Download
Manual
Eggs
API
Tests
Bugs
show
edit
You can edit this page using
wiki syntax
for markup.
Article contents:
== tick [[toc:]] === 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|Submitting ticket changes via email]] section for information on how to do that. === Author Mario Domenech Goulart === Repository [[https://code.call-cc.org/tick|https://code.call-cc.org/tick]] === Requirements tick uses [[https://git-scm.com/|git]] behind the scenes and expects it to be available on the system. Eggs: * [[/egg/commands|commands]] * [[/egg/optimism|optimism]] * [[/egg/simple-logger|simple-logger]] * [[/egg/simple-sha1|simple-sha1]] * [[/egg/srfi-1|srfi-1]] * [[/egg/srfi-13|srfi-13]] * [[/egg/xdg-basedir|xdg-basedir]] === 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 {{load}}ed 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: <enscript highlight=scheme> ;; 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)) </enscript> === 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. <enscript highlight=scheme> (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))))) </enscript> 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. <enscript highlight=scheme> (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)) </enscript> 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). <enscript highlight=scheme> (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)))) </enscript> 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}}). <enscript highlight=scheme> (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")))) </enscript> 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 [[/egg/svnwiki2html|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. <enscript highlight=scheme> (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))))))) </enscript> 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. <enscript highlight=scheme> (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")))) </enscript> 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> <procedure>(ticket-body TICKET)</procedure> <procedure>(ticket-cc TICKET)</procedure> <procedure>(ticket-changetime TICKET)</procedure> <procedure>(ticket-component TICKET)</procedure> <procedure>(ticket-difficulty TICKET)</procedure> <procedure>(ticket-id TICKET)</procedure> <procedure>(ticket-keywords TICKET)</procedure> <procedure>(ticket-milestone TICKET)</procedure> <procedure>(ticket-owner TICKET)</procedure> <procedure>(ticket-priority TICKET)</procedure> <procedure>(ticket-reporter TICKET)</procedure> <procedure>(ticket-resolution TICKET)</procedure> <procedure>(ticket-status TICKET)</procedure> <procedure>(ticket-summary TICKET)</procedure> <procedure>(ticket-time TICKET)</procedure> <procedure>(ticket-trac-id TICKET)</procedure> <procedure>(ticket-type TICKET)</procedure> <procedure>(ticket-version TICKET)</procedure> <procedure>(ticket-ref TICKET PROPERTY)</procedure> {{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)</procedure> 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)</procedure> 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)</procedure> 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)</procedure> Return the username configured in the credentials file. ==== shorten-hash <procedure>(shorten-hash hash #!optional (size 6)))</procedure> Return the first {{size}} characters of {{hash}}. ==== define-command <procedure>(define-command command usage proc)</procedure> 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: <enscript highlight=scheme> (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)))) </enscript> ==== named-listings <parameter>(named-listings)</parameter> 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)</procedure> 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). {{names}}s 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)</parameter> 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)</procedure> 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). {{names}}s 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)</parameter> List whose items are 3-element lists of: * name (symbol) * query expression (see the [[#query-expressions|Query expressions]] section) * listing format (string or {{#f}} -- see the [[#format-placeholders|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)</procedure> 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). {{names}}s 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)</procedure> 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)</procedure> 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)</procedure> Return a list of ticket ids (SHA1 hashes as strings) from the database. ==== db-add-ticket <procedure>(db-add-ticket ticket)</procedure> 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)</procedure> 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)</procedure> 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)</procedure> Return a list of strings representing the valid components as defined in the database. ==== valid-difficulties <procedure>(valid-difficulties)</procedure> Return a list of strings representing the valid estimated difficulties as defined in the database. ==== valid-milestones <procedure>(valid-milestones)</procedure> Return a list of strings representing the valid milestones as defined in the database. ==== valid-statuses <procedure>(valid-statuses)</procedure> Return a list of strings representing the valid statuses as defined in the database. ==== valid-resolutions <procedure>(valid-resolutions)</procedure> Return a list of strings representing the valid resolutions as defined in the database. ==== valid-versions <procedure>(valid-versions)</procedure> Return a list of strings representing the valid versions as defined in the database. ==== valid-priorities <procedure>(valid-priorities)</procedure> Return a list of strings representing the valid priorities as defined in the database. ==== valid-users <procedure>(valid-users)</procedure> Return a list of strings representing the valid users as defined in the database. ==== valid-types <procedure>(valid-types)</procedure> Return a list of strings representing the valid ticket types as defined in the database. === History For many years CHICKEN used [[https://trac.edgewall.org/|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
Description of your changes:
I would like to authenticate
Authentication
Username:
Password:
Spam control
What do you get when you subtract 9 from 24?