From 240406706a38bdd0e07053721d7cc54e8e6930a4 Mon Sep 17 00:00:00 2001 From: Simon Michael Date: Sat, 19 Jan 2019 15:27:44 -0800 Subject: [PATCH] move the wiki dev docs into CONTRIBUTING.md. Markdown for now. #920 [ci skip] --- CONTRIBUTING.md | 1435 +++++++++++++++++++++++++++++++++++++++++++++++ Shake.hs | 5 + doc/README | 52 ++ site/site.tmpl | 2 +- 4 files changed, 1493 insertions(+), 1 deletion(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..67afdcdf5 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,1435 @@ + + +# hledger Contributor Guide + + + +## Quick Links + +| | | +|-------------------------|----------------------------------------------------------------------------| +| IRC | [#hledger](https://kiwiirc.com/nextclient/#ircs://irc.freenode.net/#hledger), [chat log](http://ircbrowse.net/browse/hledger). Also: [#hledger-bots](https://kiwiirc.com/nextclient/#ircs://irc.freenode.net/hledger-bots), [#ledger](https://kiwiirc.com/nextclient/#ircs://irc.freenode.net/hledger), [#beancount](https://kiwiirc.com/nextclient/#ircs://irc.freenode.net/hledger) | +| Mail list | [list.hledger.org](http://list.hledger.org) | +| Twitter | [#hledger](https://twitter.com/search?q=%23hledger&src=typd&f=realtime). Also: [#plaintextaccounting](https://twitter.com/search?q=%23plaintextaccounting&src=typd&f=realtime), @LedgerTips, [#ledgercli](https://twitter.com/search?q=%23ledgercli&src=typd&f=realtime) | +| Reddit | [/r/plaintextaccounting](https://www.reddit.com/r/plaintextaccounting/) | +| Stack Exchange | [money.stackexchange.com?hledger](https://money.stackexchange.com/search?q=hledger) | +| Hacker News | [hledger mentions](https://hn.algolia.com/?query=hledger&sort=byDate&prefix&page=0&dateRange=all&type=all) | +| hledger-web demo   | [demo.hledger.org](http://demo.hledger.org) | +| hledger-api demo | [api-demo.hledger.org/api/v1/accounts](http://api-demo.hledger.org/api/v1/accounts), [api-demo.hledger.org/swagger.json](http://api-demo.hledger.org/swagger.json), [in swagger editor](http://editor2.swagger.io/#/?import=api-demo.hledger.org/swagger.json&no-proxy)
[unfinished angular sample app](http://api-demo.hledger.org) ([code](https://github.com/simonmichael/hledger/tree/master/hledger-api/examples/angular)) +| hledger interactive demo | https://hledger.alhur.es (hledger compiled to js) +| Trello | [old wishlist planning board](http://trello.hledger.org) | +| Github | [simonmichael/hledger](https://github.com/simonmichael/hledger) (shortcut: code.hledger.org), [forks](http://forked.yannick.io/simonmichael/hledger)
[commits](http://github.com/simonmichael/hledger/commits), [COMMITS!](http://starlogs.net/#simonmichael/hledger)
[open bugs](http://bugs.hledger.org), [open wishes](http://wishes.hledger.org), [open unknowns](https://github.com/simonmichael/hledger/issues?utf8=✓&q=is%3Aissue%20is%3Aopen%20-label%3A%22A%20BUG%22%20-label%3A%22A%20WISH%22%20), [open pull requests](http://prs.hledger.org), [all issues](https://github.com/simonmichael/hledger/issues?q=)
[issues with bounty tag](https://github.com/simonmichael/hledger/issues?q=label:bounty), [bountysource bounties](https://github.com/simonmichael/hledger/issues?q=%22Add%20to%20the%20bounty%20at%20Bountysource%22%20OR%20%22claim%20the%20bounty%20on%20Bountysource%22%20OR%20%22bounty%20on%20this%20issue%20has%20been%20claimed%20at%20Bountysource%22%20), [codemill bounties](https://github.com/simonmichael/hledger/issues?q=codemill), [codefund bounties](https://github.com/simonmichael/hledger/issues?utf8=✓&q=codefund)
[stars.hledger.org](http://stars.hledger.org): our rank among ~30k starred haskell projects:
2016: ->#71, 2017: ->#54, 2018 ->#53)
[github projects](https://github.com/simonmichael/hledger/projects), [waffle.io planning board](https://waffle.io/simonmichael/hledger?source=simonmichael%2Fhledger)
[](https://waffle.io/simonmichael/hledger/metrics) | +| Travis CI | [![...](https://api.travis-ci.org/simonmichael/hledger.svg?branch=master)](https://travis-ci.org/simonmichael/hledger/builds) +| Appveyor CI | [![...](https://ci.appveyor.com/api/projects/status/5vejw0w5n5igdr42?svg=true)](https://ci.appveyor.com/project/simonmichael/hledger/history) +| Hackage | packages: [hledger-lib](http://hackage.haskell.org/package/hledger-lib), [hledger](http://hackage.haskell.org/package/hledger), [hledger-ui](http://hackage.haskell.org/package/hledger-ui), [hledger-web](http://hackage.haskell.org/package/hledger-web), [hledger-api](http://hackage.haskell.org/package/hledger-api), [hledger-diff](http://hackage.haskell.org/package/hledger-diff), [hledger-iadd](http://hackage.haskell.org/package/hledger-iadd), [hledger-interest](http://hackage.haskell.org/package/hledger-interest), [hledger-irr](http://hackage.haskell.org/package/hledger-irr), [\*hledger\*](http://hackage.haskell.org/packages/search?terms=hledger)
diffs: [hledger-lib](http://hdiff.luite.com/cgit/hledger-lib/diff), [hledger](http://hdiff.luite.com/cgit/hledger/diff), [hledger-ui](http://hdiff.luite.com/cgit/hledger-ui/diff), [hledger-web](http://hdiff.luite.com/cgit/hledger-web/diff), [hledger-api](http://hdiff.luite.com/cgit/hledger-api/diff)
build status: [hledger-lib](http://matrix.hackage.haskell.org/package/hledger-lib), [hledger](http://matrix.hackage.haskell.org/package/hledger), [hledger-ui](http://matrix.hackage.haskell.org/package/hledger-ui), [hledger-web](http://matrix.hackage.haskell.org/package/hledger-web), [hledger-api](http://matrix.hackage.haskell.org/package/hledger-api)
reverse deps: [hledger-lib](http://packdeps.haskellers.com/reverse/hledger-lib), [hledger](http://packdeps.haskellers.com/reverse/hledger), [hledger-ui](http://packdeps.haskellers.com/reverse/hledger-ui), [hledger-web](http://packdeps.haskellers.com/reverse/hledger-web), [hledger-api](http://packdeps.haskellers.com/reverse/hledger-api)
bounds status:
[![...](https://img.shields.io/hackage-deps/v/hledger-lib.svg?label=hledger-lib+bounds)](http://packdeps.haskellers.com/feed?needle=hledger-lib) [![...](https://img.shields.io/hackage-deps/v/hledger.svg?label=hledger+bounds)](http://packdeps.haskellers.com/feed?needle=hledger) [![...](https://img.shields.io/hackage-deps/v/hledger-ui.svg?label=hledger-ui+bounds)](http://packdeps.haskellers.com/feed?needle=hledger-ui) [![...](https://img.shields.io/hackage-deps/v/hledger-web.svg?label=hledger-web+bounds)](http://packdeps.haskellers.com/feed?needle=hledger-web) [![...](https://img.shields.io/hackage-deps/v/hledger-api.svg?label=hledger-api+bounds)](http://packdeps.haskellers.com/feed?needle=hledger-api) | +| Stackage | [build-constraints.yaml](https://github.com/fpco/stackage/blob/master/build-constraints.yaml)
[open hledger-related issues](https://github.com/fpco/stackage/search?q=hledger+is%3Aopen&type=Issues)
packages: [hledger-lib](https://www.stackage.org/package/hledger-lib), [hledger](https://www.stackage.org/package/hledger), [hledger-ui](https://www.stackage.org/package/hledger-ui), [hledger-web](https://www.stackage.org/package/hledger-web), [hledger-api](https://www.stackage.org/package/hledger-api)
versions: [hledger-lib](https://www.stackage.org/package/hledger-lib/snapshots), [hledger](https://www.stackage.org/package/hledger/snapshots), [hledger-ui](https://www.stackage.org/package/hledger-ui/snapshots), [hledger-web](https://www.stackage.org/package/hledger-web/snapshots), [hledger-api](https://www.stackage.org/package/hledger-api/snapshots)
[![...](https://repology.org/badge/version-for-repo/stackage_nighly/hledger.svg)](https://repology.org/metapackage/hledger)| +| Repology | [quick hledger packaging status](https://repology.org/metapackage/hledger/badges), [detailed \*hledger\* packaging status](https://repology.org/metapackages/?search=hledger)
[![...](https://repology.org/badge/tiny-repos/hledger.svg)](https://repology.org/metapackage/hledger) +| Debian | source packages: [haskell-hledger-lib](http://tracker.debian.org/pkg/haskell-hledger-lib), [bugs](https://bugs.debian.org/cgi-bin/pkgreport.cgi?package=haskell-hledger-lib), [haskell-hledger](http://tracker.debian.org/pkg/haskell-hledger), [bugs](https://bugs.debian.org/cgi-bin/pkgreport.cgi?package=haskell-hledger), [haskell-hledger-ui](http://tracker.debian.org/pkg/haskell-hledger-ui), [bugs](https://bugs.debian.org/cgi-bin/pkgreport.cgi?package=haskell-hledger-ui), [haskell-hledger-web](http://tracker.debian.org/pkg/haskell-hledger-web), [bugs](https://bugs.debian.org/cgi-bin/pkgreport.cgi?package=haskell-hledger-web)
binary packages:
  stable: [hledger](https://packages.debian.org/stable/hledger), [bugs](https://bugs.debian.org/cgi-bin/pkgreport.cgi?package=hledger;dist=stable), [hledger-web](https://packages.debian.org/stable/hledger-web), [bugs](https://bugs.debian.org/cgi-bin/pkgreport.cgi?package=hledger-web;dist=stable)
  testing: [hledger](https://packages.debian.org/testing/hledger), [bugs](https://bugs.debian.org/cgi-bin/pkgreport.cgi?package=hledger;dist=testing), [hledger-web](https://packages.debian.org/testing/hledger-web), [bugs](https://bugs.debian.org/cgi-bin/pkgreport.cgi?package=hledger-web;dist=testing)
  unstable: [hledger](https://packages.debian.org/unstable/hledger), [bugs](https://bugs.debian.org/cgi-bin/pkgreport.cgi?package=hledger;dist=unstable), [hledger-ui](https://packages.debian.org/unstable/hledger-ui), [bugs](https://bugs.debian.org/cgi-bin/pkgreport.cgi?package=hledger-ui;dist=unstable), [hledger-web](https://packages.debian.org/unstable/hledger-web), [bugs](https://bugs.debian.org/cgi-bin/pkgreport.cgi?package=hledger-web;dist=unstable)
  experimental: [hledger](https://packages.debian.org/experimental/hledger), [bugs](https://bugs.debian.org/cgi-bin/pkgreport.cgi?package=hledger;dist=experimental), [hledger-ui](https://packages.debian.org/experimental/hledger-ui), [bugs](https://bugs.debian.org/cgi-bin/pkgreport.cgi?package=hledger-ui;dist=experimental), [hledger-web](https://packages.debian.org/experimental/hledger-web), [bugs](https://bugs.debian.org/cgi-bin/pkgreport.cgi?package=hledger-web;dist=experimental)
  all: [\*hledger\*](https://packages.debian.org/search?searchon=names&keywords=hledger)
[Package Tracking System help](https://www.debian.org/doc/manuals/developers-reference/resources.html#pkg-tracking-system)
popcon votes: [hledger](https://qa.debian.org/popcon.php?package=haskell-hledger), [hledger-ui](https://qa.debian.org/popcon.php?package=haskell-hledger-ui), [hledger-web](https://qa.debian.org/popcon.php?package=haskell-hledger-web), hledger active users:
[](https://qa.debian.org/popcon-graph.php?packages=hledger&show_vote=on&want_legend=on&want_ticks=on&from_date=&to_date=&hlght_date=&date_fmt=%25Y&beenhere=1) | +| Ubuntu | source packages: [haskell-hledger-lib](https://launchpad.net/ubuntu/+source/haskell-hledger-lib), [bugs](https://bugs.launchpad.net/ubuntu/+source/haskell-hledger-lib), [haskell-hledger](https://launchpad.net/ubuntu/+source/haskell-hledger), [bugs](https://bugs.launchpad.net/ubuntu/+source/haskell-hledger), [haskell-hledger-ui](https://launchpad.net/ubuntu/+source/haskell-hledger-ui), [bugs](https://bugs.launchpad.net/ubuntu/+source/haskell-hledger-ui), [haskell-hledger-web](https://launchpad.net/ubuntu/+source/haskell-hledger-web), [bugs](https://bugs.launchpad.net/ubuntu/+source/haskell-hledger-web)
binary packages: [\*hledger\*](http://packages.ubuntu.com/search?suite=all&searchon=names&keywords=hledger) | +| Gentoo | [hledger](http://gpo.zugaina.org/dev-haskell/hledger), [hledger-web](http://gpo.zugaina.org/dev-haskell/hledger-web), [\*hledger\*](http://gpo.zugaina.org/Search?search=hledger) | +| Fedora | [hledger](https://apps.fedoraproject.org/packages/hledger), [\*hledger\*](https://apps.fedoraproject.org/packages/s/hledger), [hledger (package db)](https://admin.fedoraproject.org/pkgdb/package/hledger/), [Haskell SIG](http://fedoraproject.org/wiki/Haskell_SIG) | +| Void Linux | [package search](https://voidlinux.org/packages/) -> hledger | +| Nix | [\*hledger\*](http://hydra.nixos.org/search?query=hledger) | +| Homebrew | [hledger](https://formulae.brew.sh/formula/hledger) | +| Sandstorm | [hledger web app & reviews](https://apps.sandstorm.io/app/8x12h6p0x0nrzk73hfq6zh2jxtgyzzcty7qsatkg7jfg2mzw5n90), [issues](https://github.com/simonmichael/hledger/issues?utf8=✓&q=label%3A%22platform%3A%20sandstorm%22%20) +| Reference | [GHC Can I Use](http://damianfral.github.io/ghcaniuse/) | + + + + +## About the project + +### Mission + +Why was hledger created ? + +Mainly: + +- to provide a more usable, robust, documented, cross-platform-installable version of Ledger for users +- to provide a more maintainable and hackable version of Ledger for developers + +Also: + +- to provide a useful library and toolbox for finance-minded haskell programmers +- to explore the suitability of Haskell for such applications +- to experiment with building a successful time-and-money-solvent project in a thriving ecosystem of financial software projects + +What is the hledger project's current mission ? + +1. Provide peace of mind: bring clarity, relief, and peace of mind to folks stressed, confused, overwhelmed by finances. +2. Educate and empower: help individuals and communities achieve clarity, accountability and mastery with money and time. + +### Roles and activities + +- newcomer/potential user +- user +- library user +- field tester +- bug wrangler +- support +- documentor +- qa +- developer +- packager +- communicator +- project manager + + + +## Getting started + +New contributors are always welcome in the hledger project. +Jump in! Or [ask us](http://hledger.org/docs.html#helpfeedback) to help you find a task. + +### Funder + +Become a financial backer to +sustain and grow this project, +increase your influence, +express gratitude, +build prosperity consciousness, +and help transform world finance! + +- Use the donate links on the [home page](/) +- Configure a recurring donation +- Contribute or pledge bounties on issues you care about +- Ask your organization to contribute +- Work on project sustainability, accountability, fundraising + +### Tester + +- Test installation on platforms you have access to +- Test examples, advice, and links in the docs +- Run the latest release or developer build in daily use +- Run [[tests|Developer-workflows#run-package-tests]] +- Run [[benchmarks|Developer-workflows#run-package-benchmarks]] +- Report packaging, documentation, UX, functional, performance issues +- Report and help analyse problems via irc/mail list/bug tracker + +When reporting bugs, don't forget to search the tracker for a similar bug report. +Otherwise, open a new bug by clicking "New issue", or . + +Enhancement requests are sometimes added to the tracker,but for these consider using +the IRC channel and mail list (see [Getting help](/docs.html#getting-help)). +Both are archived and linkable, so the idea won't be lost. +There is also a collection of wishes at the old [trello board](http://trello.hledger.org). + +### Technical Writer + +- get familiar with the website and documentation online, review and test +- get familiar with the site/doc source files (see [Shake.hs](#shake)) +- get the latest hledger source +- send patches with names prefixed with "doc: " (or "site: ") + +### Graphics Designer + +- more/better logos & graphics +- illustrations and diagrams +- web design mockups for home page, site, hledger-web UI + + +### Communicator + +Marketing and market understanding is vital. + +- clarify project goals, value proposition, brand, mission, story +- monitor product-market fit +- identify new opportunities +- influence developer priorities +- spread the word! + +### Maintainer + +#### Help with issue management + +- watch tracker activity, report status +- apply/update labels where needed +- follow up on dormant issues +- facilitate a consistently good bug-reporting & PR-contributing experience + +#### Help with packaging + +- package hledger for linux distros, macports, etc. +- develop mac/windows installers +- find and assist distro packagers/installer developers + +#### Help with project management + +- clarify/update goals and principles +- monitor, report on project progress and performance +- research, compare and report on successful projects, related projects +- identify collaboration opportunities +- marketing, communication, outreach +- release management, roadmap planning + +### Developer + +See [[Developer workflows]] + + + + + +## Make + +A Makefile is provided to make common developer tasks easy to remember, +and to insulate us a little from the ever-evolving Haskell tools ecosystem. +Using it is entirely optional, but recommended. +You'll need [GNU Make](http://www.gnu.org/software/make) installed. + +The Makefile contains a fair amount of obsolete cruft and needs cleanup. Some tasks (docs, website) are now handled by the [[Shake]] file instead. + +The Makefile is self-documenting. Run `make` to see a list of the main make rules: + +```shell +$ make +Makefile:37: -------------------- hledger make rules -------------------- +Makefile:39: make [help] -- list documented rules in this makefile. make -n RULE shows more detail. +Makefile:204: (INSTALLING:) +Makefile:206: make install -- download dependencies and install hledger executables to ~/.local/bin or equivalent (with stack) +Makefile:231: (BUILDING:) +Makefile:235: make build -- download dependencies and build hledger executables (with stack) +Makefile:304: make hledgerdev -- quickly build the hledger executable (with ghc and -DDEVELOPMENT) +... +``` + +To see what a make rule will do without actually doing it, use the `-n` flag: + +```shell +$ make build -n +stack build +``` +```shell +$ make test -n +(stack test \ + && echo pkgtest PASSED) || echo pkgtest FAILED +(stack exec hledger test \ + && echo builtintest PASSED) || echo builtintest FAILED +(COLUMNS=80 PATH=`pwd`/bin:/home/simon/src/hledger/bin:/home/simon/.local/bin:/home/simon/.cabal/bin:/opt/ghc/7.10.1/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/var/lib/gems/1.9.1/bin stack exec -- shelltest --execdir -- -j16 --hide-successes tests \ + && echo functest PASSED) || echo functest FAILED +``` + + +## Shake + +`Shake.hs` in the top directory complements the Makefile; it is used for some more complex tasks, such as building documentation and the web site. + +Compile it: + + ./Shake.hs # or, make Shake + +See help: + + ./Shake + + +## Developer workflows + +### Get developer tools + +Ensure [`stack`](https://haskell-lang.org/get-started) is installed +(or if you’re a [cabal](https://www.haskell.org/cabal/) expert, feel free to use that.) + +Ensure [`git`](http://git-scm.com) is installed. On Windows, it comes with stack. + +Here are some useful optional tools: + +- [GNU Make](http://www.gnu.org/software/make): to use the convenient [Make rules](#make). +- [`entr`](http://www.entrproject.org/) runs arbitrary commands when files change. +- [`ghcid`](http://hackage.haskell.org/package/ghcid) gives real-time GHC feedback as you make code changes. +- [`shelltestrunner`](http://hackage.haskell.org/package/shelltestrunner) runs hledger's functional tests. +- [`quickbench`](http://hackage.haskell.org/package/quickbench) measures and reports time taken by commands. +- [`hasktags`](http://hackage.haskell.org/package/hasktags) generates tag files for quick code navigation in editors like Emacs and vim. +- For browsing and editing Haskell code, popular tools include: Emacs, Vim, IDEA, VS Code, Atom.. + +Eg: + + stack install ghcid shelltestrunner quickbench hasktags + brew install entr + +### Get the code + + git clone https://github.com/simonmichael/hledger + cd hledger + +### Review code + +- review and discuss new [pull requests](http://prs.hledger.org) and commits on github +- build hledger and test the latest changes in your own repo +- read the existing [code docs and source](#quick-links) +- send feedback or discuss via [IRC or mail list](/docs.html#helpfeedback) + +### Build in place + +See also http://hledger.org/download.html#c.-build-the-development-version . + + stack build # hledger hledger-ui ... + +This fetches the required GHC version and haskell dependencies from the default stackage snapshot (configured in `stack.yaml`), +then builds all hledger packages. +This can take a while! To save time, you can build individual packages, eg just the CLI and TUI. + +Note stack does not fetch C libraries such as curses or terminfo, which you might need to install yourself, using your system's package manager. +In case of trouble, see [download](/download.html#link-errors). + +If you want to use an older snapshot/GHC for some reason, specify one of the older stack-ghc*.yaml files: + + stack --stack-yaml stack-ghc8.2.yaml build + +### Run in place + + stack exec -- hledger # ARGS... + stack exec -- hledger-ui # ARGS... + stack exec -- which hledger + +### Build and install + +This builds and also copies the hledger executables to `~/.local/bin` or the Windows equivalent +(which you should [add to your `$PATH`](/download.html#b)). + + stack install # hledger hledger-ui ... + +### Run package tests + +Runs any HUnit/doctest/easytest tests defined by each hledger package. + + stack test # hledger ... + +### Run package benchmarks + +Runs any performance reports defined by each hledger package. + + stack bench # hledger ... + +### Run quickbench benchmarks + +Times the end-user commands in `bench.sh` using quickbench. + + make bench + +### Run functional tests + +Runs the shelltestrunner tests defined in tests/, which test the hledger CLI. + + stack build hledger + make functest + +### Run haddock tests + +Checks for anything that would break haddock doc generation. + + make haddocktest + +Checks for the unit-tests embedded in documentation. + + make doctest + +### Simulate Travis tests + +Locally runs tests similar to what we run on Travis CI. + + make travistest + +### Test with all supported GHC versions/stackage snapshots + + make allsnapshotstest + +### Use GHCI + +GHCI is GHC's REPL, useful for exploring and calling code interactively. + +#### Get a GHCI prompt for hledger-lib: + + cd hledger-lib; stack ghci hledger-lib + +Changing into the package directory isn't actually needed, but it +enables a custom .ghci script which sets a more useful short prompt. + +#### Get a GHCI prompt for hledger: + + cd hledger; stack ghci hledger + +#### Get a GHCI prompt for hledger-ui: + + cd hledger-ui; stack ghci hledger-ui + +#### Get a GHCI prompt for hledger-web: + + cd hledger-web; stack ghci hledger-web + +hledger-web also needs to find some things in its current directory (like the static/ directory). +This normally just works, if not please [send details](https://github.com/simonmichael/hledger/issues/274). + +### Add a test + +- identify what to test +- choose the test type: unit ? functional ? benchmark ? +- currently expected to pass or fail ? +- figure out where it goes +- write test, verify expected result +- get it committed + +### Fix a bug or add a feature + +- research, discuss, validate the issue/feature on irc/list/bug tracker +- look for related tests, run the tests and check they are passing +- add a test ? +- develop a patch +- include any related issue numbers in the patch name, eg: "fix for blah blah (#NNN)" +- get it committed + +### Get your changes accepted + +Follow the usual github workflow: + +- fork the main hledger repo on github, +- git clone it to your local machine, +- git commit, after (?) pulling and merging the latest upstream changes +- git push back to github, +- open a pull request on github, +- follow up on any discussion there. + +If you're new to this process, [help.github.com](http://help.github.com) may be useful. + +### Add yourself to the contributor list + +- after getting something into the master branch, read and sign the [contributor list & agreement](contributors.html). Or, [ask](/docs.html#helpfeedback) to be added. +- give yourself a high five! + +### Work on docs + +Most docs tasks are handled by [[Shake]]. + +#### List Shake rules: + + ./Shake + +#### Generate man/info/txt manuals (in hledger*/) and embed in hledger executables: + + ./Shake manuals + stack build + +#### Generate html manuals and the hledger website (in site/_site/): + + ./Shake website + +#### To remove all files generated by Shake: + + ./Shake Clean + +### Use ghcid for watching GHC/GHCI + +[ghcid](http://hackage.haskell.org/package/ghcid) is the most reliable and fastest way to see GHC's feedback, and optionally run tests or a GHCI command, as you edit. We run it via make, for convenience and to watch multiple packages rather than just one. Run `make help-ghcid` to list related rules. + +#### Watch for compile errors in hledger-lib and hledger: + + make ghcid + +#### Watch compile errors and the output of some hledger command: + + ghcid -c 'make ghci' -T ':main -f a.j bal --budget -N' + +### Use --file-watch for watching stack + +stack's --file-watch flag will re-run build/test/bench when source files or package.yaml/cabal files change. Eg: + + stack test hledger --file-watch + +If you find that adding --fast makes this any faster, please update this. + +### Use entr for watching arbitrary commands + +[entr](http://entrproject.org/) is the most robust cross-platform tool for watching files and running a command when they change. Note its first argument must be an executable program, to run a shell command or multiple commands use `bash -c "..."`. + +#### Rerun a single functional test as you change it: + + ls tests/budget/budget.test | entr bash -c 'clear; COLUMNS=80 stack exec -- shelltest --execdir tests/budget/budget.test -i12' + + + + +## Code + +hledger is a suite of applications, tools and libraries. +The main hledger code repository is [github.com/simonmichael/hledger](http://github.com/simonmichael/hledger) +(shortcut url `code.hledger.org`). +There are also various hledger addons maintained as separate projects with their own repos. + +### hledger packages + +Within the main repo, there are a number of separate cabal packages, +making it easier to pick and choose parts of hledger to install or to package. +They are: + +#### hledger-lib + +[package](http://hackage.haskell.org/package/hledger-lib), +[code](https://github.com/simonmichael/hledger/tree/master/hledger-lib) + +Core data models, parsing, standard reports, and utilities. +Most data types are defined in [Hledger.Data.Types](http://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html), +while functions that operate on them are defined in Hledger.Data.TYPENAME. +Under [Hledger.Read](https://github.com/simonmichael/hledger/tree/master/hledger-lib/Hledger/Read.hs) +are parsers for the supported input formats. +Data files are parsed into a +[Journal](http://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:Journal), +which contains a list of +[Transactions](http://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:Transaction), +each containing multiple +[Postings](http://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:Posting) +of some +[MixedAmount](http://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:MixedAmount) +(multiple +single-[CommoditySymbol](http://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:CommoditySymbol) +[Amounts](http://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:Amount)) +to some +[AccountName](http://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:AccountName). +When needed, the Journal is further processed to derive a +[Ledger](http://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:Ledger), +which contains summed +[Accounts](http://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:Account). +In [Hledger.Reports](http://hackage.haskell.org/package/hledger-lib/docs/Hledger-Reports.html) +there are standard reports, which extract useful data from the Journal or Ledger. + +Here's a diagram of the main data model: + + +diagram + + + + +#### hledger + +[package](http://hackage.haskell.org/package/hledger), +[code](https://github.com/simonmichael/hledger/tree/master/hledger), +[manual](http://hledger.org/manual.html#hledger) + +hledger's command line interface, and command line options and utilities for other hledger tools. + +Try tracing the execution of a hledger command: + +1. [Hledger.Cli.Main:main](https://github.com/simonmichael/hledger/blob/master/hledger/Hledger/Cli/Main.hs#L302) +parses the command line to select a command, then +2. gives it to +[Hledger.Cli.Utils:withJournalDo](https://github.com/simonmichael/hledger/blob/master/hledger/Hledger/Cli/Utils.hs#L73), +which runs it after doing all the initial parsing. +3. Parsing code is under +[hledger-lib:Hledger.Read](https://github.com/simonmichael/hledger/tree/master/hledger-lib/Hledger/Read.hs), +eg [Hledger.Read.JournalReader](https://github.com/simonmichael/hledger/tree/master/hledger-lib/Hledger/Read/JournalReader.hs). +4. Commands extract useful information from the parsed data model using +[hledger-lib:Hledger.Reports](https://github.com/simonmichael/hledger/tree/master/hledger-lib/Hledger/Reports), +and +5. render in plain text for console output (or another output format, like CSV). +6. Everything uses the data types and utilities from +[hledger-lib:Hledger.Data](https://github.com/simonmichael/hledger/tree/master/hledger-lib/Hledger/Data) +and [hledger-lib:Hledger.Utils](https://github.com/simonmichael/hledger/blob/master/hledger-lib/Hledger/Utils.hs). + +#### hledger-ui + +[package](http://hackage.haskell.org/package/hledger-ui), +[code](https://github.com/simonmichael/hledger/tree/master/hledger-ui), +[manual](http://hledger.org/manual.html#hledger-ui) + +A curses-style text interface. + +#### hledger-web + +[package](http://hackage.haskell.org/package/hledger-web), +[code](https://github.com/simonmichael/hledger/tree/master/hledger-web), +[manual](http://hledger.org/manual.html#hledger-web) + +A web interface. +hledger-web starts a web server built with the yesod framework, +and (by default) opens a web browser view on it. +It reads the journal file(s) at startup and again whenever they change. +It can also write (append) new transactions to the journal file. + +There are two main views, which can be filtered with +[queries](http://hledger.org/manual.html#queries): + +- [/journal](http://demo.hledger.org/journal), showing general journal entries (like `hledger print`) + +- [/register](http://demo.hledger.org/register?q=inacct:Expenses:Food), + showing transactions affecting an account (slightly different from + hledger's [register](http://hledger.org/manual.html#register) command, which shows postings). + +There is also: + +- a sidebar (toggled by pressing `s`) showing the chart of accounts (like `hledger balance`) +- an [add form](http://demo.hledger.org/journal?add=1) for adding new transactions (press `a`) +- a help dialog showing quick help and keybindings (press `h` or click ?) + +Most of the action is in + +- [config/routes](https://github.com/simonmichael/hledger/tree/master/hledger-web/config/routes) +- [templates/default-layout-wrapper.hamlet](https://github.com/simonmichael/hledger/tree/master/hledger-web/templates/default-layout-wrapper.hamlet) +- [Foundation](https://github.com/simonmichael/hledger/tree/master/hledger-web/Foundation.hs) +- [Handler.*](https://github.com/simonmichael/hledger/tree/master/hledger-web/Handler) +- [static/hledger.js](https://github.com/simonmichael/hledger/tree/master/hledger-web/static/hledger.js) +- [static/hledger.css](https://github.com/simonmichael/hledger/tree/master/hledger-web/static/hledger.css) + +Handler module and function names end with R, like the yesod-generated route type they deal with. + +Dynamically generated page content is mostly inline hamlet. +Lucius/Julius files and widgets generally are not used, except for the default layout. + +Here are some ways to run it during development: + +- `yesod devel`: runs in developer mode, rebuilds automatically when config, template, static or haskell files change +(but only files in the hledger-web package): +```shell +$ (cd hledger-web; yesod devel) +``` + +- [yesod-fast-devel](https://hackage.haskell.org/package/yesod-fast-devel) + may be a good alternative, also reloads the browser page + +- `stack ghci`: runs the server in developer mode from GHCI. +Changes to static files like hledger.js will be visible on page reload; +to see other changes, restart it as shown. +```shell +$ (cd hledger-web; stack ghci hledger-web) +hledger-web> :main --serve # restart: ctrl-c, :r, enter, ctrl-p, ctrl-p, enter +``` + +- `make ghci-web`: runs the server in developer mode from GHCI, also +interprets the hledger-lib and hledger packages so that :reload picks +up changes in those packages too: +```shell +$ make ghci-web +ghci> :main --serve +``` +(This rule also creates symbolic links to hledger-web's `config`, `messages`, `static` and `templates` +directories, needed in developer mode, so it can run from the top directory. This may not work on Windows.) + +#### hledger-api + +[package](http://hackage.haskell.org/package/hledger-api), +[code](https://github.com/simonmichael/hledger/tree/master/hledger-api), +[manual](http://hledger.org/manual.html#hledger-api) + +A web API server. Uses the servant framework. + +### Quality + +Relevant tools include: + +- unit tests (HUnit, make unittest) +- functional tests (shelltestrunner, make functest) +- performance tests (simplebench, make bench) +- documentation tests (make haddocktest + manual) +- ui tests (manual) +- installation tests (manual) +- code reviews + +### Code review + +- Code review party 2014/7/21-25: + [discussion](http://thread.gmane.org/gmane.comp.finance.ledger.hledger/1070) +- Dev sprint/party 2015/10/10: + [discussion](http://thread.gmane.org/gmane.comp.finance.ledger.hledger/1254) + + +## Pull requests + +Most contributed hledger code (and some of the project maintainer's code) +is submitted and reviewed via Github pull requests. +Here are some tips for contributing PRs to hledger. + +### Code review is important + +We aim to improve and sustain hledger's quality and maintainability over the long term. + +Many PRs, especially small ones, and even some big ones, can be merged quickly. +We love merging good PRs quickly. + +Some bigger or more risky PRs can require substantial review, discussion, changes, or re-submission. +Sometimes this is a bigger task than the coding. +Much valuable design, quality control, and knowledge sharing happens at this time. +Some PRs get rejected, but their discussion and exploration can still be a useful contribution. +We very much want to avoid wasted work, but it occasionally happens. +Our process is evolving and imperfect. +All of this is normal. + +We hope you'll see it as a golden opportunity to collaborate with experts, +share and receive knowledge, refine your design/documentation/code, +and practice real-world development and communication skills. +Patience and persistence pays. + +### The pull request + +A PR should have a clear purpose, documented in its description. Mention any #ISSUENOs addressed. + +Don't tackle too much at once. +Smaller/more focussed PRs can be reviewed quicker and accepted (or rejected) quicker. + +Consider showing a draft of documentation first (more on this below). + +### The commit(s) + +Commits should be easy to review. +Ideally each commit is complete, and has a single clear purpose, +which should be documented in the summary (and long description, if needed). +\#ISSUENOs can be mentioned in summary/description too when appropriate. + +Within the above constraint, fewer, larger commits are preferred. + +Keep in mind that commit messages are valuable documentation +for future developers and troubleshooters. +They are also the starting point for package changelogs and hledger release notes. +High-quality commit messages makes the release process quicker, and the resulting docs better. + +User-impacting commits should mention the user-visible changes, +and be described in user-relevant language. +Library-user-impacting commits, eg API changes, ideally will also +be called out, and can described in more technical language. +Commits affecting hledger internals are less important, +but you may notice some adhoc conventions if you browse the history. +In particular, you can optionally prefix the summary with short component codes (cf [[Issues]]) +to facilitate history reading and changelog/release note production. + +Rewrite and force-push your commits freely (rebase -i, push -f) to clean them up. +Unless we decide to squash the PR into one commit, +your commits will become part of hledger's history "for all time", +so think about future developers trying to understand them, git bisect, etc. + +Rebase your commits against latest master for easiest review. Especially if they start to conflict. + +### The docs + +PRs should include appropriate updates to reference documentation, unless otherwise agreed. +Typically this means the manual source files (hledger*/hledger*.m4.md). +It can also involve +command line option names and descriptions, +other --help output, +hledger's commands list, +hledger-ui's help dialog, +hledger-web's help dialog, +etc. +Sometimes it means the developer docs, at least the ones in the main repo (READMEs). + +Reviewers can understand your PR more efficiently once proposed doc changes are provided, and may postpone it otherwise. +We are happy to help with the docs if needed - just ask. + +Updating rendered manuals (hledger.{1,info,txt,md,html}) is not required, and probably best avoided to reduce conflicts. +Updating other docs such as tutorials, how-tos, examples, or screenshots is not required, +though it's welcome (may be in a different repo). + +### Documentation first + +hledger follows documentation-driven design. +It is in fact highly effective, and highly recommended, +to write the new docs (help text/reference manual/haddocks/developer README..) before writing any code. +You can share a rough draft on IRC, on the mail list, in an issue comment, +or in a "WIP" PR starting with just the proposed docs commit. + +This is often the quickest road to getting something merged into hledger. +hledger's many parts interact in surprisingly complex ways. +The documentation-driven working style lets us discuss, clarify and reach a good-enough consensus economically, +after which coding/review/acceptance can go quicker. + + + +## Tests + +About testing in the hledger project, as of 201809. + +### Kinds of tests + +1. Unit tests + + Unit tests exercise small chunks of functionality. In hledger, that + means a function. So, many of our functions have one or more unit + tests. These are mostly in hledger-lib, with a few in hledger. + + Our unit tests use + [easytest](http://hackage.haskell.org/package/easytest) and some + helpers from + [Hledger.Utils.Test](https://github.com/simonmichael/hledger/blob/master/hledger-lib/Hledger/Utils/Test.hs), + and follow a consistent pattern which aims to keep them + + - easy to read (clear, concise) + - easy to write (low boilerplate, low cognitive load) + - easy to maintain (easy to edit, easy to refactor, robust) + - easy to associate with the code under test (easy to view/jump + between code & test, easy to estimate coverage) + - and scalable (usable for all devs, easy to run and select, + suitable for small/large modules/packages). + + Here\'s the pattern (let us know if you see a better way): + + ``` {.haskell} + module Foo ( + ... + tests_Foo -- export this module's and submodules' tests + ) + where + import Hledger -- provides Hledger.Utils.Test helpers + import Bar -- submodules, providing tests_Bar etc. + import Baz + + functionA = ... + functionB = ... + functionC = ... + functionD = ... + + tests_Foo = tests "Foo" [ -- define tests at the end of each module + + tests "functionA" [ + test "a basic test" $ expect SOMEBOOL + ,test "a pretty equality test" $ SOMEEXPR `is` EXPECTEDVALUE + ,test "a pretty parsing test" $ expectParseEq PARSER INPUT EXPECTEDRESULT + ,test "a sequential test" $ do + A `is` B + io $ doSomeIO + C `is` D + ,_test "an ignored test" $ ... -- will be skipped + ] + + ,tests "functionB" [ + it "does blah" $ ... -- alternate spelling for test/_test + ,_it "will bleh" $ ... + ] + + ,tests "functionC" [ + expect BOOL -- unnamed tests (harder to identify/select) + ,EXPR `is` VALUE + ] + + ,_tests "functionD" [ -- will be skipped + ... + ] + + ,tests_Foo -- aggregate submodule tests + ,tests_Bar + ] + ``` + + Here are + [some](https://github.com/simonmichael/hledger/blob/master/hledger-lib/Hledger/Data/Posting.hs#L296) + real-world + [examples](https://github.com/simonmichael/hledger/blob/master/hledger-lib/Hledger/Read/JournalReader.hs#L579). + + The unit tests are shipped as part of the hledger executable, and + can always be run via the [test](http://hledger.org/manual#test) + command (`hledger test`). + + Here\'s the quick way to run unit tests while developing:\ + `make ghcid-test` or `make ghcid-test-Hledger.Some.Module`. + +2. Doc tests + + Like unit tests, but defined inside functions\' haddock + documentation, in the style of a GHCI transcript. These test + functionality, provide usage examples in the API docs, and test + those examples, all at once. They are a bit more finicky and slower + than unit tests. See + [doctest](http://hackage.haskell.org/package/doctest) for more. + + doctests [do not work on Mac with GHC + 8.4+](https://github.com/sol/doctest/issues/199), out of the box. + See + [ghc\#15105](https://ghc.haskell.org/trac/ghc/ticket/15105#comment:10) + for current status and a workaround. + +3. Functional tests + + Functional tests test the overall functioning of the program. For + hledger, that means running `hledger` with various inputs and + options and checking for the expected output. This exercises + functionality in the hledger and hledger-lib packages. We do this + with + [shelltestrunner](http://hackage.haskell.org/package/shelltestrunner). + Tests are defined in files under + [tests/](https://github.com/simonmichael/hledger/tree/master/tests), + grouped by *component* (command or topic name). + +4. Code tests + + We have some tests aimed at testing eg code quality, generally + defined as make rules, such as: + + --------------------- ------------------------------------------------------------------------------------- + `make haddocktest` can haddock process all code docs without error + `make buildtest` does all code build warning free with the default GHC version & stackage snapshot + `make buildtestall` does the code build warning free with all supported GHC versions/stackage snapshots + --------------------- ------------------------------------------------------------------------------------- + + See below for examples. + +5. Package test suites + + Haskell tools like stack and cabal recognise test suites defined in + a package\'s cabal file (or package.yaml file). These can be run via + `stack test`, `cabal test` etc., and they are required to build and + pass by services like Stackage. Here are the currently hledger + package test suites: + + ------------- ------------ --------------------------------------------------------------- + package test suite what it runs + hledger-lib doctests doctests + hledger-lib easytests unit tests + hledger test builtin test command (hledger\'s + hledger-lib\'s unit tests) + hledger-ui + hledger-web + hledger-api + ------------- ------------ --------------------------------------------------------------- + +### Coverage + +This means how thoroughly the code is tested - both in breadth (are all +parts of the code tested at least a little ?) and in depth (are all +possible code paths, states, situations tested ?). + +Our current test coverage can be summarised like so: + + ------------- ------ ----- ------------ + package unit doc functional + hledger-lib X X X + hledger X X + hledger-ui + hledger-web + hledger-api + ------------- ------ ----- ------------ + +There are ways to generate detailed coverage reports for haskell unit +tests, at least. It would be useful to set this up for hledger. + +### How to run tests + +Run unit tests: + +``` {.example} +$ make unittest +``` + +Run doctests: + +``` {.example} +$ make doctest +``` + +Run functional tests (and unit tests, now): + +``` {.example} +$ stack install shelltestrunner +$ make functest +``` + +Run the package tests (unit tests, maybe doctests, but not functional +tests) of all or selected packages. + +``` {.example} +$ stack test [PKG] +``` + +Run \"default tests: package plus functional tests\": + +``` {.example} +$ make test +``` + +Test generation of haddock docs: + +``` {.example} +$ make haddocktest +``` + +Thorough test for build issues with current GHC: + +``` {.example} +$ make buildtest +``` + +Thorough test for build issues with all supported GHC versions: + +``` {.example} +$ make buildtestall +``` + +Run built-in hledger/hledger-lib unit tests via hledger command: + +``` {.example} +$ hledger test # test installed hledger +$ stack build hledger && stack exec -- hledger test # test just-built hledger +$ hledger test --help +test [TESTPATTERN] [SEED] + Run the unit tests built in to hledger-lib and hledger, + printing results on stdout and exiting with success or failure. + Tests are run in two batches: easytest-based and hunit-based tests. + If any test fails or gives an error, the exit code will be non-zero. + If a pattern argument (case sensitive) is provided, only easytests + in that scope and only hunit tests whose name contains it are run. + If a numeric second argument is provided, it will set the randomness + seed for easytests. +``` + +Rebuild and rerun hledger/hledger-lib unit tests via ghcid: + +``` {.example} +$ make ghcid-test +``` + +Rebuild and rerun only some tests via ghcid (see hledger test --help): + +``` {.example} +$ make ghcid-test-TESTPATTERN +``` + +See all test-related make rules: + +``` {.example} +$ make help-test +``` + + + + +## Benchmarks + +Benchmarks are standard performance measurements, +which we define using `bench` declarations in cabal files. +There is [one in hledger.cabal](https://github.com/simonmichael/hledger/blob/master/hledger/hledger.cabal#L228), +with related code and data files in [hledger/bench/](https://github.com/simonmichael/hledger/tree/master/hledger/bench). + +To run the standard hledger benchmark, use `stack bench hledger`. +This installs haskell dependencies (but not system dependencies) and rebuilds as needed, +then runs [hledger/bench/bench.hs](https://github.com/simonmichael/hledger/blob/master/hledger/bench/bench.hs), +which by default shows quick elapsed-time measurements for several operations on a standard data file: + +```shell +$ stack bench hledger +NOTE: the bench command is functionally equivalent to 'build --bench' +... +hledger-0.27: benchmarks +Running 1 benchmarks... +Benchmark bench: RUNNING... +Benchmarking hledger in /Users/simon/src/hledger/hledger with timeit +read bench/10000x1000x10.journal [1.57s] +print [1.29s] +register [1.92s] +balance [0.21s] +stats [0.23s] +Total: 5.22s +Benchmark bench: FINISH +``` + +bench.hs has some other modes, which you can use by compiling and running it directly. +`--criterion` reports more detailed and dependable measurements, but takes longer: + +```shell +$ cd hledger; stack exec -- ghc -ibench bench/bench && bench/bench --criterion +... +Linking bench/bench ... +Benchmarking hledger in /Users/simon/src/hledger/hledger with criterion +benchmarking read bench/10000x1000x10.journal +time 1.414 s (1.234 s .. 1.674 s) + 0.996 R² (0.989 R² .. 1.000 R²) +mean 1.461 s (1.422 s .. 1.497 s) +std dev 59.69 ms (0.0 s .. 62.16 ms) +variance introduced by outliers: 19% (moderately inflated) + +benchmarking print +time 1.323 s (1.279 s .. 1.385 s) + 1.000 R² (0.999 R² .. 1.000 R²) +mean 1.305 s (1.285 s .. 1.316 s) +std dev 17.20 ms (0.0 s .. 19.14 ms) +variance introduced by outliers: 19% (moderately inflated) + +benchmarking register +time 1.995 s (1.883 s .. 2.146 s) + 0.999 R² (0.998 R² .. NaN R²) +mean 1.978 s (1.951 s .. 1.995 s) +std dev 25.09 ms (0.0 s .. 28.26 ms) +variance introduced by outliers: 19% (moderately inflated) + +benchmarking balance +time 251.3 ms (237.6 ms .. 272.4 ms) + 0.998 R² (0.997 R² .. 1.000 R²) +mean 260.4 ms (254.3 ms .. 266.5 ms) +std dev 7.609 ms (3.192 ms .. 9.638 ms) +variance introduced by outliers: 16% (moderately inflated) + +benchmarking stats +time 325.5 ms (299.1 ms .. 347.2 ms) + 0.997 R² (0.985 R² .. 1.000 R²) +mean 329.2 ms (321.5 ms .. 339.6 ms) +std dev 11.08 ms (2.646 ms .. 14.82 ms) +variance introduced by outliers: 16% (moderately inflated) +``` + +`--simplebench` shows a table of elapsed-time measurements for the commands defined in [bench/default.bench](https://github.com/simonmichael/hledger/blob/master/hledger/bench/default.bench). +It can also show the results for multiple h/ledger executables side by side, if you tweak the bench.hs code. +Unlike the other modes, it does not link with the hledger code directly, but runs the "hledger" executable found in $PATH (so ensure that's the one you intend to test). + +```shell +$ cd hledger; stack exec -- ghc -ibench bench/bench && bench/bench --simplebench +Benchmarking /Users/simon/.local/bin/hledger in /Users/simon/src/hledger/hledger with simplebench and shell +Using bench/default.bench +Running 4 tests 1 times with 1 executables at 2015-08-23 16:58:59.128112 UTC: +1: hledger -f bench/10000x1000x10.journal print [3.27s] +1: hledger -f bench/10000x1000x10.journal register [3.65s] +1: hledger -f bench/10000x1000x10.journal balance [2.06s] +1: hledger -f bench/10000x1000x10.journal stats [2.13s] + +Summary (best iteration): + ++-----------------------------------------++---------+ +| || hledger | ++=========================================++=========+ +| -f bench/10000x1000x10.journal print || 3.27 | +| -f bench/10000x1000x10.journal register || 3.65 | +| -f bench/10000x1000x10.journal balance || 2.06 | +| -f bench/10000x1000x10.journal stats || 2.13 | ++-----------------------------------------++---------+ +``` + +bench's --simplebench mode is based on a standalone tool, [tools/simplebench.hs](https://github.com/simonmichael/hledger/blob/master/tools/simplebench.hs). +simplebench.hs is a generic benchmarker of one or more executables (specified on the command line) against one or more sets of command-line arguments (specified in a file). +It has a better command-line interface than bench.hs, so you may find it more convenient +for comparing multiple hledger versions, or hledger and ledger. Eg: + +```shell +$ stack exec -- ghc tools/simplebench +[1 of 1] Compiling Main ( tools/simplebench.hs, tools/simplebench.o ) +Linking tools/simplebench ... +``` +```shell +$ tools/simplebench -h +tools/simplebench -h +simplebench: at least one executable needed +bench [-f testsfile] [-n iterations] [-p precision] executable1 [executable2 ...] + +Run some functional tests with each of the specified executables, +where a test is "zero or more arguments supported by all executables", +and report the best execution times. + + -f testsfile --testsfile=testsfile file containing tests, one per line, default: bench.tests + -n iterations --iterations=iterations number of test iterations to run, default: 2 + -p precision --precision=precision show times with this precision, default: 2 + -v --verbose show intermediate results + -h --help show this help + +Tips: +- executables may have arguments if enclosed in quotes +- tests can be commented out with # +- results are saved in benchresults.{html,txt} +``` +```shell +cd hledger; $ ../tools/simplebench -f bench/default.bench hledger ledger +Using bench/default.bench +Running 4 tests 2 times with 2 executables at 2015-08-24 04:24:37.257068 UTC: + +Summary (best iteration): + ++-----------------------------------------++---------+--------+ +| || hledger | ledger | ++=========================================++=========+========+ +| -f bench/10000x1000x10.journal print || 3.24 | 0.43 | +| -f bench/10000x1000x10.journal register || 3.80 | 3.48 | +| -f bench/10000x1000x10.journal balance || 2.05 | 0.18 | +| -f bench/10000x1000x10.journal stats || 2.10 | 0.19 | ++-----------------------------------------++---------+--------+ +``` + +Finally, for quick, fine-grained performance measurements when troubleshooting or optimising, I use +[dev.hs](https://github.com/simonmichael/hledger/blob/master/dev.hs). + + + +## Sample journals + +Synthetic data files like `examples/100x100x10.journal` are useful for benchmarks and testing. +The numbers describe the number of transactions, number of accounts, and maximum account depth respectively. +They are generated by [`tools/generatejournal.hs`](https://github.com/simonmichael/hledger/blob/master/tools/generatejournal.hs). +They should get built automatically as needed, if not you can use `make samplejournals`: + +```shell +$ make samplejournals +ghc tools/generatejournal.hs +[1 of 1] Compiling Main ( tools/generatejournal.hs, tools/generatejournal.o ) +Linking tools/generatejournal ... +tools/generatejournal 100 100 10 >examples/100x100x10.journal +tools/generatejournal 1000 1000 10 >examples/1000x1000x10.journal +tools/generatejournal 1000 10000 10 >examples/1000x10000x10.journal +tools/generatejournal 10000 1000 10 >examples/10000x1000x10.journal +tools/generatejournal 10000 10000 10 >examples/10000x10000x10.journal +tools/generatejournal 100000 1000 10 >examples/100000x1000x10.journal +tools/generatejournal 3 5 5 >examples/ascii.journal +tools/generatejournal 3 5 5 --chinese >examples/chinese.journal +tools/generatejournal 3 5 5 --mixed >examples/mixed.journal +``` + + +## Docs + +https://github.com/simonmichael/hledger/tree/master/doc + +Project documentation lives in a number of places: + +- `site/*.md` is the hledger.org website content, which is generated with hakyll[-std] and pandoc +- haddock documentation in the code appears on Hackage +- short blurbs: cabal files, module headers, HCAR, GSOC project, .. +- `doc/notes.org` has some old developer notes +- developer reports (profiles, benchmarks, coverage..) in doc/profs, sometimes published at hledger.org/profs + +How to prepare changelogs & release notes + +Draft: + +- `make changelog-draft >> doc/CHANGES.draft.org` (or `>` if this is the first draft) +- open this org file and sort the nodes (`C-c ^ a`) +- categorisation pass 1: go through and add topic prefixes where missing +- sort the nodes again +- categorisation pass 2: move significant items to the appropriate package subnode as appropriate; keep "soft" items that might appear in release notes; delete the rest +- cleanup pass: combine/rewrite items for clarity + +Changelogs: + +- choose release version and date +- add new sections at top of {doc,hledger*}/CHANGES +- move the items from CHANGES.draft.org to these CHANGES files +- remove CHANGES.draft.org + +Release notes: + +- add a new TOC entry and section in site/release-notes.md +- copy/rewrite/summarise package changelogs +- note any other items of interest +- list release contributors +- write release summary + + +## Issues + +The hledger project\'s issue tracker is on github. It contains: + +- BUG issues - failures in some part of the hledger project (the main + hledger packages, docs, website..) +- WISH issues - feature proposals, enhancement requests +- uncategorised issues - we don\'t know what these are yet +- pull requests - proposed changes to code and docs + +### Issue Urls + +- - show open BUG issues +- - show open WISH issues +- - show all issues, open or closed +- - show open pull requests +- - create a new issue + +### Labels + +Labels are used to categorise: + +- the issue\'s type: \"A BUG\" or \"A WISH\", in shades of red (The A + makes it appear as first label) +- relevant subsystems/topics, in light blue. More about this below. +- relevant platforms, in light purple +- resolution if not fixed: + \"closed:cant-reproduce/duplicate/invalid/wont-fix\", in dark grey +- \"bounty\", in bright yellow: issues with bountysource funding +- \"easy?\", in dim yellow: issues which are probably relatively easy + to fix +- \"imported\" etc., in white: miscellaneous information + +### Components + +Issues and the hledger project generally are organised into components: +mostly non-overlapping topics, one for each user command, add-on tool, +input format, output format, etc. Each component gets a light blue label +in the issue tracker. Component names (sometimes abbreviated) are used +as a prefix to commit messages, and to organise changelogs and release +notes. Below are the current components, and their open issues. + +### Custodians + +If you are interested in helping with a particular component for a +while, please add yourself as a custodian in the issues table below. A +custodian\'s job is to help manage the issues, rally the troops, and +drive the open issue count towards zero. The more custodians, the +better! By dividing up the work this way, we can scale and make forward +progress. + +### Open issues + + + + --------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------ ----------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------- + COMPONENT (DESCRIPTION, CUSTODIANS) BUGS (starter=good first issue) WISHES PRS ? + + [all](https://github.com/simonmichael/hledger/issues?q=is:open) ([@simonmichael](https://github.com/simonmichael/)) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22) + [install](https://github.com/simonmichael/hledger/issues?q=is:open+label:install) (hledger-install script) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:install) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:install)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:install)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:install)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:install) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:install) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:install) + [cli](https://github.com/simonmichael/hledger/issues?q=is:open+label:cli) (hledger tool) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:cli) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:cli)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:cli)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:cli)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:cli) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:cli) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:cli) + [ui](https://github.com/simonmichael/hledger/issues?q=is:open+label:ui) (hledger-ui tool) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:ui) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:ui)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:ui)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:ui)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:ui) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:ui) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:ui) + [web](https://github.com/simonmichael/hledger/issues?q=is:open+label:web) (hledger-web tool) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:web) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:web)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:web)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:web)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:web) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:web) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:web) + [api](https://github.com/simonmichael/hledger/issues?q=is:open+label:api) (hledger-api tool) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:api) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:api)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:api)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:api)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:api) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:api) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:api) + [interest](https://github.com/simonmichael/hledger/issues?q=is:open+label:interest) (hledger-interest tool) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:interest) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:interest)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:interest)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:interest)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:interest) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:interest) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:interest) + [journal](https://github.com/simonmichael/hledger/issues?q=is:open+label:journal) (journal format) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:journal) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:journal)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:journal)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:journal)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:journal) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:journal) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:journal) + [timeclock](https://github.com/simonmichael/hledger/issues?q=is:open+label:timeclock) (timeclock format) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:timeclock) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:timeclock)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:timeclock)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:timeclock)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:timeclock) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:timeclock) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:timeclock) + [timedot](https://github.com/simonmichael/hledger/issues?q=is:open+label:timedot) (timedot format) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:timedot) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:timedot)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:timedot)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:timedot)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:timedot) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:timedot) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:timedot) + [csv](https://github.com/simonmichael/hledger/issues?q=is:open+label:csv) (CSV format, CSV output) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:csv) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:csv)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:csv)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:csv)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:csv) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:csv) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:csv) + [json](https://github.com/simonmichael/hledger/issues?q=is:open+label:json) (JSON output) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:json) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:json)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:json)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:json)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:json) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:json) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:json) + [html](https://github.com/simonmichael/hledger/issues?q=is:open+label:html) (HTML output) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:html) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:html)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:html)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:html)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:html) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:html) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:html) + [accounts](https://github.com/simonmichael/hledger/issues?q=is:open+label:accounts) (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:accounts) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:accounts)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:accounts)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:accounts)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:accounts) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:accounts) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:accounts) + [activity](https://github.com/simonmichael/hledger/issues?q=is:open+label:activity) (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:activity) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:activity)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:activity)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:activity)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:activity) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:activity) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:activity) + [add](https://github.com/simonmichael/hledger/issues?q=is:open+label:add) (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:add) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:add)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:add)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:add)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:add) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:add) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:add) + [balance](https://github.com/simonmichael/hledger/issues?q=is:open+label:balance), bal (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:balance) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:balance)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:balance)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:balance)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:balance) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:balance) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:balance) + [balancesheet](https://github.com/simonmichael/hledger/issues?q=is:open+label:balancesheet), bs (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:balancesheet) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:balancesheet)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:balancesheet)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:balancesheet)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:balancesheet) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:balancesheet) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:balancesheet) + [cashflow](https://github.com/simonmichael/hledger/issues?q=is:open+label:cashflow), cf (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:cashflow) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:cashflow)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:cashflow)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:cashflow)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:cashflow) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:cashflow) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:cashflow) + [checkdates](https://github.com/simonmichael/hledger/issues?q=is:open+label:checkdates) (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:checkdates) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:checkdates)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:checkdates)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:checkdates)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:checkdates) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:checkdates) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:checkdates) + [checkdupes](https://github.com/simonmichael/hledger/issues?q=is:open+label:checkdupes) (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:checkdupes) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:checkdupes)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:checkdupes)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:checkdupes)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:checkdupes) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:checkdupes) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:checkdupes) + [close](https://github.com/simonmichael/hledger/issues?q=is:open+label:close) (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:close) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:close)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:close)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:close)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:close) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:close) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:close) + [import](https://github.com/simonmichael/hledger/issues?q=is:open+label:import) (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:import) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:import)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:import)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:import)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:import) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:import) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:import) + [incomestatement](https://github.com/simonmichael/hledger/issues?q=is:open+label:incomestatement), is (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:incomestatement) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:incomestatement)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:incomestatement)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:incomestatement)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:incomestatement) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:incomestatement) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:incomestatement) + [prices](https://github.com/simonmichael/hledger/issues?q=is:open+label:prices) (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:prices) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:prices)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:prices)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:prices)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:prices) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:prices) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:prices) + [print](https://github.com/simonmichael/hledger/issues?q=is:open+label:print) (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:print) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:print)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:print)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:print)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:print) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:print) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:print) + [printunique](https://github.com/simonmichael/hledger/issues?q=is:open+label:printunique) (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:printunique) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:printunique)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:printunique)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:printunique)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:printunique) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:printunique) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:printunique) + [register](https://github.com/simonmichael/hledger/issues?q=is:open+label:register), reg (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:register) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:register)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:register)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:register)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:register) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:register) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:register) + [registermatch](https://github.com/simonmichael/hledger/issues?q=is:open+label:registermatch) (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:registermatch) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:registermatch)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:registermatch)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:registermatch)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:registermatch) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:registermatch) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:registermatch) + [rewrite](https://github.com/simonmichael/hledger/issues?q=is:open+label:rewrite) (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:rewrite) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:rewrite)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:rewrite)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:rewrite)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:rewrite) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:rewrite) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:rewrite) + [roi](https://github.com/simonmichael/hledger/issues?q=is:open+label:roi) (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:roi) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:roi)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:roi)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:roi)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:roi) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:roi) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:roi) + [stats](https://github.com/simonmichael/hledger/issues?q=is:open+label:stats) (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:stats) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:stats)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:stats)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:stats)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:stats) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:stats) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:stats) + [tags](https://github.com/simonmichael/hledger/issues?q=is:open+label:tags) (command) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:tags) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:tags)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:tags)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:tags)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:tags) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:tags) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:tags) + [balcmds](https://github.com/simonmichael/hledger/issues?q=is:open+label:balcmds) (bal/bs/bse/cf/is) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:balcmds) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:balcmds)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:balcmds)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:balcmds)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:balcmds) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:balcmds) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:balcmds) + [budget](https://github.com/simonmichael/hledger/issues?q=is:open+label:budget) (balance --budget, budgeting) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:budget) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:budget)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:budget)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:budget)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:budget) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:budget) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:budget) + [periodexpressions](https://github.com/simonmichael/hledger/issues?q=is:open+label:periodexpressions) (-b, -e, -p, date:) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:periodexpressions) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:periodexpressions)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:periodexpressions)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:periodexpressions)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:periodexpressions) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:periodexpressions) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:periodexpressions) + [tags](https://github.com/simonmichael/hledger/issues?q=is:open+label:tags) (using tags) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:tags) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:tags)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:tags)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:tags)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:tags) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:tags) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:tags) + [doc](https://github.com/simonmichael/hledger/issues?q=is:open+label:doc) (documentation, help) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:doc) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:doc)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:doc)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:doc)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:doc) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:doc) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:doc) + [site](https://github.com/simonmichael/hledger/issues?q=is:open+label:site) (website, web presence) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:site) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:site)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:site)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:site)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:site) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:site) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:site) + [tools](https://github.com/simonmichael/hledger/issues?q=is:open+label:tools) (developer tools, infrastructure) [bugs](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:tools) ([starter](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+label:%22good+first+issue%22+label:tools)*[easy](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+label:easy?+label:tools)*[other](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+BUG%22+-label:%22good+first+issue%22+-label:easy?+label:tools)) [wishes](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+label:%22A+WISH%22+label:tools) [PRs](https://github.com/simonmichael/hledger/issues?q=is:open+is:pr+label:tools) [?](https://github.com/simonmichael/hledger/issues?q=is:open+is:issue+-label:%22A+BUG%22+-label:%22A+WISH%22+label:tools) + --------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------ ----------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------- + +### Milestones and Projects + +Milestones are used a little bit to plan releases. In 2017 we +experimented with projects, but in 2018 milestones are in favour again.. + +### Estimates + +You might see some experiments in estimate tracking, where some issue +names might have a suffix noting estimated and spent time. Basic format: +\[ESTIMATEDTOTALTASKTIME\|TIMESPENTSOFAR\]. Examples: \`\`\` \[2\] two +hours estimated, no time spent \[..\] half an hour estimated (a dot is +\~a quarter hour, as in timedot format) \[1d\] one day estimated (a day +is \~4 hours) \[1w\] one week estimated (a week is \~5 days or \~20 +hours) \[3\|2\] three hours estimated, about two hours spent so far +\[1\|1w\|2d\] first estimate one hour, second estimate one week, about +two days spent so far \`\`\` Estimates are always for the total time +cost (not time remaining). Estimates are not usually changed, a new +estimate is added instead. Numbers are very approximate, but better than +nothing. + +### Trello + +The [trello board](http://trello.hledger.org) (trello.hledger.org) is an +old collection of wishlist items. This should probably be considered +deprecated. + +## Funding + +My vision for the hledger project has always been for it to be "accountable" and "self-sustaining", possibly through new forms of incentivisation. +Classic non-monetary FOSS communities are a beautiful and precious thing. +Adding money can change their dynamic. +Yet, we would enjoy having a lot more issues resolved, and a faster rate of progress. +So we experiment, gently. + +Currently we use bounties as a way to encourage resolution of issues. +There are a few ways to do this: + +1. You or your organisation can offer a bounty simply by saying so on the issue. + +2. You can use Bountysource. A few hledger bounties have been completed there. + +3. You can use the new Open Collective process below. + +Issues with bounties of any kind are marked with the `bounty` label. +The Bounty Manager is @simonmichael. + +### New bounty process + +It currently looks like this, and will evolve: + +- Issues are marked as bounties by @simonmichael. Feel free to suggest additional issues which should receive the bounty label. + +- Bounties are paid from the hledger project's public Open Collective fund. + By contributing to the fund as an individual or organisation, you enable more bounties. + +- These OC bounties (unlike 1 and 2 above) have standard amounts. + These may be adjusted over time, depending eg on the state of our funds. + Our current bounty amounts are + - level 1: 10 USD + - level 2: 25 USD + - level 3: 50 USD + +- When you complete a bounty, submit an expense to Open Collective, + for whichever of the above bounty amounts you think appropriate, + based eg on time or expertise spent, how much you need it, + how much remains in our fund for other bounties, etc. + This will be reviewed by OC and (maybe ?) @simonmichael. + Successful claims, like donations, will appear in our public OC ledger. + +Our bounty amounts are small, and nothing like professional rates in most countries, +but they still establish a principle of sustainability, +and help us to experiment. +You are encouraged to claim your bounties, +though you can also choose to transfer them to a new issue of your choice. + diff --git a/Shake.hs b/Shake.hs index db62991c5..ba77f9aa5 100755 --- a/Shake.hs +++ b/Shake.hs @@ -154,6 +154,8 @@ main = do , "index" , "intro" , "release-notes" + , "README" + , "CONTRIBUTING" ] ] ++ [ prefix "manual" <.> "html" @@ -279,6 +281,9 @@ main = do "--lua-filter tools/pandoc-demote-headers.lua" ">>" webmancombined :: Action ExitCode + -- Copy some extra markdown files from the main repo into the site. + ["site/README.md", "site/CONTRIBUTING.md"] |%> \out -> + copyFile' (dropDirectory1 out) (map toLower out) -- WEBSITE HTML & ASSETS diff --git a/doc/README b/doc/README index 94f78193c..25304f761 100644 --- a/doc/README +++ b/doc/README @@ -95,3 +95,55 @@ Could do it for past releases but no pressing need. In site/release-notes.md, we stopped mentioning minor releases around 0.27. The old minor releases should probably be removed or promoted to the same heading level as major releases. + + +## 201901 docs reorg (#920, WIP) + +https://groups.google.com/forum/#!topic/hledger/t2nVr3zER8Q/discussion + +> > On Oct 26, 2018, at 1:47 PM, Simon Michael wrote: +> > +> > A quick heads-up: I am feeling like stepping back from github wiki, and reorganising our docs like so: +> > +> > Two repos: +> > +> > 1. hledger - code and hard docs +> > +> > - code and code docs (haddock docs & doctest examples) +> > - developer docs (READMEs in md or org format) +> > - product manuals (hledger*/hledger*.m4.md) +> > - release notes and announcements +> > - HCAR entries +> > +> > 2. hledger-site - website and soft docs +> > +> > - hledger.org content, resources, site infrastructure +> > - user cookbook, how-tos, articles +> > - links to blog posts, discussions etc. +> > - other resources relating to our web presence/marketing +> > +> > If you disagree, let's discuss. Some quick considerations: +> > +> > - moving docs to the wiki hasn't affected the contribution rate +> > - using the wiki increases our dependence on github and makes our work less self-contained and future-proof +> > - the wiki docs don't look great, aren't very flexible, & don't integrate well with our site & static docs +> > - using two docs systems increases complexity +> > - dev docs in the wiki are too far from the code, and compete with READMEs +> +> PS: +> +> - Why not go back to just one repo for everything ? Or if two repos, why not put all docs in one of them ? +> +> Dev docs are most discoverable and maintainable right there in the main repo, ie as READMEs. Likewise for API docs (haddocks) and the reference manuals (hledger*/hledger*m4.md). We want all of these updated in lock step with code/tooling changes. +> +> Other ("soft") docs are needed, but these have a more relaxed process, schedule, and scope (eg bookkeeping advice). They occasionally generate a lot of noise in the commit log, and I think it's a good to keep that out of the code history. The website (home and other pages, site design, site infrastructure) generates similar commit storms and is somewhat independent of code, so it goes in the soft docs repo too. +> +> These are my thoughts, but I have an open mind if you see a better way. +> +> me (Simon Michael (sm) change) +> 10/27/18 +> Still plenty of time to discuss and reconsider, but see also +> https://github.com/simonmichael/hledger/issues/920 +> https://github.com/simonmichael/hledger/issues/921 +> +> I'll probably make a start on the first one (consolidating dev docs in main repo). diff --git a/site/site.tmpl b/site/site.tmpl index a3c847a00..68ac8461f 100644 --- a/site/site.tmpl +++ b/site/site.tmpl @@ -48,7 +48,7 @@