;;; tissue --- Text based issue tracker ;;; Copyright © 2022, 2023 Arun Isaac ;;; ;;; This file is part of tissue. ;;; ;;; tissue is free software: you can redistribute it and/or modify it ;;; under the terms of the GNU General Public License as published by ;;; the Free Software Foundation, either version 3 of the License, or ;;; (at your option) any later version. ;;; ;;; tissue is distributed in the hope that it will be useful, but ;;; WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;; General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with tissue. If not, see . (use-modules (doc skribilo)) (document :title [tissue] (toc) (chapter :title [Introduction] :ident "chapter-introduction" (p [tissue is an issue tracker and project information management system built on plain text files and git. It is specifically intended for small free software projects. It features a static site generator to build a project website and a powerful search interface to search through project issues and documentation. The search interface is built on the ,(ref :url "https://xapian.org/" :text "Xapian search engine library"), and is available both as a command-line program and as a web server.])) (chapter :title [Tutorial] :ident "chapter-tutorial" (p [In this tutorial, we will learn how to create issues for an existing project, and how to publish those issues on the web.]) (section :title [Creating issues] (p [We start with a git repository for our project.]) (prog :line #f [~/my-project$]) (p [The repository presumably has project source code committed in it. We now create a directory ,(file "issues") and populate it with a few issues.]) (prog :line #f [~/my-project$ mkdir issues]) (p [Each issue is a ,(ref :url "https://gemini.circumlunar.space/docs/cheatsheet.gmi" :text "gemtext") file. Let's write our first issue ,(file "issues/crash-on-invalid-query.gmi") and commit it.]) (prog :line #f [# Search engine crashes on invalid query * tags: bug * assigned: Arun Isaac When a syntatically invalid search query is entered into the search engine, the search engine process crashes. Further queries all return a 500 Internal Server Error.]) (prog :line #f [~/my-project$ git add issues/crash-on-invalid-query.gmi ~/my-project$ git commit -m "Add first issue"]) (p [Let's add a second issue ,(file "issues/add-emacs-interface.gmi") and commit it.]) (prog :line #f [# Add Emacs interface * tags: feature-request Add Emacs interface to search for issues.]) (prog :line #f [~/my-project$ git add issues/add-emacs-interface.gmi ~/my-project$ git commit -m "Add second issue"]) (p [Now that we have a couple of issues, let's tell tissue about them. We do this using a configuration file ,(file "tissue.scm").]) (prog :line #f [(tissue-configuration #:indexed-documents (map read-gemtext-issue (gemtext-files-in-directory "issues")))]) (prog :line #f [~/my-project$ git add tissue.scm ~/my-project$ git commit -m "Add tissue configuration"]) (p [This tells tissue to index all files in the ,(file "issues") directory as issues. The ,(code "gemtext-files-in-directory") function returns a list of filenames (strings) in the ,(file "issues") directory. The ,(code "read-gemtext-issue") function reads each file and returns an ,(code "") object. Now, we may list all issues on the command line using tissue.]) (prog :line #f [~/my-project$ tissue Add Emacs interface feature-request ISSUE issues/add-emacs-interface.gmi opened 70 minutes ago by Arun Isaac Search engine crashes on invalid query (assigned: Arun Isaac) ISSUE issues/crash-on-invalid-query.gmi opened 71 minutes ago by Arun Isaac]) (p [We could also search through and shortlist. The search interface is a powerful full text search engine powered by the excellent ,(ref :url "https://xapian.org/" :text "Xapian") library.]) (prog :line #f [~/my-project$ tissue search assigned:arun Search engine crashes on invalid query (assigned: Arun Isaac) ISSUE issues/crash-on-invalid-query.gmi opened 76 minutes ago by Arun Isaac ~/my-project$ tissue search emacs Add Emacs interface feature-request ISSUE issues/add-emacs-interface.gmi opened 87 minutes ago by Arun Isaac Add Emacs interface * tags: feature-request Add Emacs interface to search for... ~/my-project$ tissue search tag:bug Search engine crashes on invalid query bug (assigned: Arun Isaac) ISSUE issues/crash-on-invalid-query.gmi opened 88 minutes ago by Arun Isaac])) (section :title [Publishing issues on the web] (p [Now, let's try to get our issue tracker on the web. tissue does not treat issue files specially. You will notice that it only speaks of ,(emph "indexed documents") and not specifically about issues. We need to explicitly tell it to create web pages for each issue file and to associate each issue to its respective web page. We do this with the following ,(file "tissue.scm") configuration.]) (prog :line #f [(tissue-configuration #:indexed-documents (map (lambda (filename) (slot-set (read-gemtext-issue filename) 'web-uri (string-append "/" (string-remove-suffix ".gmi" filename)))) (gemtext-files-in-directory "issues")) #:web-files (map (lambda (filename) (file (replace-extension filename "html") (gemtext-exporter filename))) (gemtext-files-in-directory "issues")))]) (prog :line #f [~/my-project$ git add tissue.scm ~/my-project$ git commit -m "Add web configuration"]) (p [The ,(code "#:indexed-documents") keyword argument is the same as earlier, but in addition, we set the ,(code "'web-uri") slot of the ,(code "") object to the HTTP URI at which the issue may be found.]) (p [In order to actually put web pages for each issue at the aforementioned HTTP URIs, we need the ,(code "#:web-files") keyword argument. The ,(code "#:web-files") keyword argument takes a list of ,(code "") objects that describe files on the website. Each file object constitutes a file path and something we call a ,(emph "writer function"). A writer function is a one-argument function that accepts a port and writes the contents of a file to it. Here, we use the ,(code "gemtext-exporter") function provided by tissue to create a writer function for each issue file.]) (p [Now, to see this in a web interface, run]) (prog :line #f [~/my-project$ tissue web-dev Tissue development web server listening at http://localhost:8080]) (p [Visiting ,(ref :url "http://localhost:8080/") on your browser should get you the web search interface.])) ;; TODO (section :title [Production deployment] (p [TODO]))) (chapter :title [Gemtext markup for issues] :ident "chapter-gemtext-markup" (p [Issues must be written in ,(ref :url "https://gemini.circumlunar.space/docs/gemtext.gmi" :text "gemtext markup") with added extra notation to specify issue metadata.]) (p [Tag issues.] (prog :line #f [* tags: enhancement, good first issue])) (p [Close issues. Use either of] (prog :line #f [* closed]) (prog :line #f [* status: closed])) (p [Assign issues to one or more people.] (prog :line #f [* assigned: mekalai]) (prog :line #f [* assigned: muthu, mekalai])) (p [Create task lists with regular gemtext lists starting with ,(code "[ ]"). Tasks may be marked as completed by putting an ,(code "x") within the brackets, like so: ,(code "[x]")] (prog :line #f [* \[x\] Do this. * \[ \] Then, do this. * \[ \] Finally, do this.]))) (chapter :title [Reference] :ident "chapter-reference" (section :title [Object and record constructors] :ident "section-constructors" (description (docstring-function-documentation "tissue/tissue.scm" 'tissue-configuration) (function-documentation 'file [filename writer] [Construct a ,(code []) object that represents an output file to be created.] (description (item :key (var [filename]) [the name of the file to create as a string]) (item :key (var [writer]) [a one-argument function that takes a port as an argument and writes data destined for ,(var [filename]) into that port]))))) (section :title [Reader functions] :ident "section-reader-functions" (p [These functions produce ,(code []) objects (or objects of classes inheriting from ,(code [])) by reading files or other data sources.]) (description (docstring-function-documentation "tissue/issue.scm" 'read-gemtext-issue) (docstring-function-documentation "tissue/file-document.scm" 'read-gemtext-document) (docstring-function-documentation "tissue/commit.scm" 'commits-in-current-repository))) (section :title [Writer functions] :ident "section-writer-functions" [These functions write output files and are meant to be specified in a ,(code []) object.] (description (docstring-function-documentation "tissue/web/static.scm" 'copier) (docstring-function-documentation "tissue/web/static.scm" 'gemtext-exporter) (docstring-function-documentation "tissue/web/static.scm" 'skribe-exporter))) (section :title [Utility functions] :ident "section-utility-functions" [These miscellaneous functions are useful when writing tissue configuration files.] (description (docstring-function-documentation "tissue/tissue.scm" 'gemtext-files-in-directory) (docstring-function-documentation "tissue/git.scm" 'git-tracked-files) (docstring-function-documentation "tissue/document.scm" 'slot-set)))))