48 KiB
- toc
Developer guide
Contributors of all levels are most welcome in the hledger project. This guide is action-oriented: below you'll find useful links, then procedures, then general info.
Quick links
How to..
Do user testing
- review and critique our documentation and web presence
- test the procedures on download and on this page
- check that the hledger binaries run your platform, and
hledger test
reports no failures - test the hledger tools' functionality, usability, browser compatibility, ui layout etc.
- discuss/report problems via irc/mail list/bug tracker
Get help
- for quick help or if you're not sure about the problem, you can ask on the #hledger (irc.hledger.org) IRC channel or the mail list (list.hledger.org). If #hledger does not respond quickly, you can leave the window open and check back later, or leave your email address.
- check the open and closed issues in the bug tracker (bugs.hledger.org). Sometimes the problem has been fixed in git but not yet released.
Suggest enhancements
Suggestions and feature requests are welcome, but we don't want them to pile up in the issue tracker obscuring higher-priority tasks. So consider these alternatives:
- The #hledger (irc.hledger.org) IRC channel on freenode and the mail list (list.hledger.org) are excellent places for discussing and refining ideas. Both are archived and linkable, so the idea won't be lost. IRC is quick (if I'm not online, leave a comment anyway), while the mail list has the most readers.
- The trello board (trello.hledger.org) is a good place for storing and prioritising backlog and wishlist items of all kinds.
- The bug tracker (bugs.hledger.org) on github is mostly used for reporting problems with the existing product or docs. If you're not sure if it's a problem or not, it's fine to report it. When enhancement requests land in the bug tracker, they get the WISH label, and are excluded from the default view at bugs.hledger.org.
Report bugs
- get to know the bug tracker (on github) and its contents.
Some convenient url shortcuts:
bugs.hledger.org
- show non-wishlist issues
bugs.hledger.org/N
- jump to issue #N - research and update existing issues
- report a bug:
bugs.hledger.org/new
Install stack and git
stack is the recommended tool for building hledger from source. It builds haskell projects, installing required haskell libraries as needed. It can also install GHC (the compiler) and (on windows) git, if needed.
You don't need to use stack, if you are already expert with the older cabal tool, or even just GHC, but I won't attempt to document those procedures; these docs assume you have downloaded and installed stack.
On Windows, you should choose the 64-bit stack download if you will be processing >50,000 transactions at a time with hledger (#275).
git is the revision control tool you'll need to fetch the latest hledger source and submit changes. On windows, stack can install it for you. These docs assume you have installed git and know a little about how to use it.
Install other optional tools
Up-to-date alex
, happy
, and haddock
tools are required, but stack
should install those for you.
Here are some optional extra tools:
shelltestrunner
is useful for running functional tests.hasktags
is an easy way to generate editor tag files for quick source code navigation.profiteur
is for reporting stack profiles.hpack
regenerates cabal files when package.yaml files have been updated.hoogle
is for searching source code.
You can install them all with:
$ stack install shelltestrunner hasktags profiteur hpack hoogle
Get the latest hledger source
$ git clone code.hledger.org hledger # aka github.com/simonmichael/hledger.git
$ cd hledger
Use the Makefile
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 installed.
The Makefile is self-documenting. Run make
to see a list of the main make rules:
$ 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:
$ make build -n
stack build
$ 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/src/hledger/extra:/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
Build or install hledger
Run make
to see a list of build rules. You probably want build
or install
.
make build
runs stack build, which downloads required haskell
dependencies and builds all hledger packages.
The resulting executables will be somewhere under .stack-work, eg in
.stack-work/install/i386-linux/lts-3.0/7.10.2/bin/
.
$ make build
stack build
hledger-lib-0.27: configure
hledger-lib-0.27: build
hledger-lib-0.27: install
hledger-0.27: configure
hledger-0.27: build
Progress: 1/4
...
Note stack will install required haskell libraries, but not C libraries such as curses or terminfo. If you get a build error, it is likely because one of these is missing, in which case you must identify and install it yourself using your system's package manager. This is usually a bit harder on Windows.
make install
runs stack install, which does everything stack build does and also
copies the executables to ~/.local/bin
or the Windows equivalent.
You should make sure this directory is in your $PATH
, so that you can just type
hledger
to run the latest.
$ make install
stack install
NOTE: the install command is functionally equivalent to 'build --copy-bins'
hledger-0.27: build
...
Copied executables to /Users/simon/.local/bin/:
- hledger-web
- hledger-ui
- hledger
You can save time and effort by building just the package(s) you're interested in. To install just the hledger command-line tool, but not hledger-ui or (especially costly) hledger-web, do:
$ stack install hledger
(This looks like the download page command for installing the latest hledger release from Stackage. The difference is, here we are running it inside the hledger source tree, so the source version will be installed.)
Run benchmarks
Benchmarks are standard performance measurements,
which we define using bench
declarations in cabal files.
There is one in hledger.cabal,
with related code and data files in 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,
which by default shows quick elapsed-time measurements for several operations on a standard data file:
$ 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:
$ 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.
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).
$ 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. 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:
$ stack exec -- ghc tools/simplebench
[1 of 1] Compiling Main ( tools/simplebench.hs, tools/simplebench.o )
Linking tools/simplebench ...
$ 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}
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.
Generate sample journal files
Synthetic data files like data/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
.
They should be built as needed, if not you can use make samplejournals
rule:
$ 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 >data/100x100x10.journal
tools/generatejournal 1000 1000 10 >data/1000x1000x10.journal
tools/generatejournal 1000 10000 10 >data/1000x10000x10.journal
tools/generatejournal 10000 1000 10 >data/10000x1000x10.journal
tools/generatejournal 10000 10000 10 >data/10000x10000x10.journal
tools/generatejournal 100000 1000 10 >data/100000x1000x10.journal
tools/generatejournal 3 5 5 >data/ascii.journal
tools/generatejournal 3 5 5 --chinese >data/chinese.journal
tools/generatejournal 3 5 5 --mixed >data/mixed.journal
Run developer tests
This command will install haskell dependencies (you might need to install additional system dependencies yourself) and run the package test suites. Currently these consist of hledger-lib's unit tests, hledger's unit tests, and hledger-web's functional tests:
$ stack test [PKG]
Run the hledger-lib and hledger unit tests as a built-in hledger command:
$ [stack exec] hledger test
Run the hledger functional tests:
$ stack install shelltestrunner # if not already done
$ make functest
Run both unit and functional tests:
$ make test
Test haddock doc generation:
$ make haddocktest
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
Use the REPL (GHCI)
These all work from the main hledger source directory (at least).
First, ensure all required dependencies are installed with these commands. (You might also need to install some system libs like terminfo or curses.)
$ stack test
$ stack bench
Get a GHCI prompt for hledger-lib:
$ stack ghci hledger-lib
Get a GHCI prompt for hledger:
$ stack ghci hledger
Get a GHCI prompt for 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.
Get a GHCI prompt for hledger and hledger-lib:
$ make ghci
Get a GHCI prompt for hledger-web, hledger and hledger-lib:
$ make ghci-web
Improve the documentation
- get familiar with the website and documentation online, review and test
- get familiar with the site/doc source files (see Makefile)
- get the latest hledger source
- send patches with names prefixed with "doc: " (or "site: ")
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 may be useful.
Become a contributor
- after getting one or more patches committed, read and sign the contributor list & agreement
- or, ask to be added
Do code review
- review and discuss new pull requests and commits on github
- set up for development and test the latest changes in your own repo
- read the existing code docs and source
- send feedback or discuss via irc or list
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
Do a major release
-
review the release how-to in the developer guide
- and update as needed (make site-preview, http://localhost:8000/developer-guide.html#do-a-major-release)
-
clean working copy
- commit/stash/clear any pending changes in working copy
- merge any commits from other branches & working copies
- check out master, or release branch. Major releases are done in master if possible. If not, do as much of the below as is feasible in master, then start a release branch (git checkout -b X.Y)
-
ensure tests pass
- make unittest
- make functest
- make haddocktest
- make cabalfiletest
-
update dependencies
- check & fix any outdated upper bounds (dev guide -> quick links -> hackage)
-
update cabal files
- /hledger.cabal
- descriptions
- tested-with
- file lists
- data-files
- extra-tmp-files
- extra-source-files
- exposed-modules
- other-modules
- /hledger.cabal
-
update stack.yaml file
- resolver
- extra-deps
- flags
-
update docs
- haddocks
- changelogs
- man pages
- site/release-notes.md
- site/manual.md (commands, options, --help, ledger compatibility..)
- site/developer-guide.md
- site/step-by-step.md
- site/how-to-*
- site/faq.md (ledger compatibility)
- site/download.md
- site/contributors.md
- doc/ANNOUNCE
-
update version
- edit .version
- make setversion
- double-check: cabal files, man pages ?, manual, download, release-notes, devguide..
- commit
-
make tarballs/binaries
- ensure no packages are commented out in Makefile's PACKAGES
- make cabalsdist
- [make windows binaries]
- [make mac binaries]
-
release tests
- make haddocktest
- make cabalfiletest
- cabal tarballs install into a clean directory without warnings
- cabal upload --dry reports no problems
-
tag
- make tagrelease
-
publish
- stack upload hledger-lib; stack upload hledger; stack upload hledger-ui; stack upload hledger-web
- [wait a day for it to appear in stackage nightly ?]
- ensure hackage is showing the latest haddocks
- check the hackage build matrix
- git push --tags
- deploy at demo.hledger.org
- [upload binaries to hledger.org]
- ensure the website is showing latest docs (download links, release notes, manual, how-tos, dev guide links, etc.)
-
announce
- review/close open issues in tracker
- email doc/ANNOUNCE to hledger, haskell-cafe, haskell, [ledger] lists
- tweet
- [blog]
- [reddit]
- update release notes with announcement link & short description
-
post-release
- handle problem reports, support requests
Do a minor release
Differences from a major release: work in a release branch, set PACKAGES only to the affected package(s), don't run make setversion.
- cleanup
- review working copies (laptop, server, website) & branches, commit pending changes
- document
- */*.cabal for affected package(s) (descriptions, tested-with, files..)
- */CHANGES for affected package(s)
- site/release-notes.md
- site/manual.md (commands, options, --help, ledger compatibility..)
- site/step-by-step.md
- site/how-to-*
- test
- make unittest
- make functest
- make haddocktest
- branch
- switch to release branch (git checkout X.Y)
- version
- edit .version (don't make setversion)
- manually bump version for affected package(s): cabal files, manual..
- package
- set Makefile's PACKAGES to affected package(s)
- make cabalsdist
- test
- install from tarball(s) into a clean directory
- tag
- make tagrelease
- push
- git push --tags
- upload
- make cabalupload
- announce
- [email hledger]
- [tweet]
Project overview
Mission, principles, goals
The hledger project aims to produce:
- a practical, accessible, dependable tool for end users
- a useful library and toolbox for finance-minded haskell programmers
- a successful, time-and-money-solvent project within a thriving ecosystem of financial software projects.
Roles and activities
- newcomer/potential user
- user
- library user
- field tester
- bug wrangler
- support
- documentor
- qa
- developer
- packager
- communicator
- project manager
Documentation
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
Code
The hledger repo is hosted on Github, at http://github.com/simonmichael/hledger.
You can also jump there via code.hledger.org[/commits]
.
Quality control
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 reviews
We have held one code review party, in July 2014, on the mail list and IRC channel. Here's the original proposal giving some motivation, and the discussion logs, note these are a good source of hledger development tips:
- 2014/7/21-25 hledger-web code & UI mail thread, IRC log
Dev sprint/party #2 was on 2015/10/10.
Implementation notes
hledger
There are two core cabal packages:
hledger-lib - data model, parsing, manipulation, standard reports
(github)
hledger - command line interface, reusable cli options & helpers
(github)
Most data types are defined in hledger-lib:Hledger.Data.Types, while functions that operate on them are defined in hledger-lib:Hledger.Data.TYPENAME. Here's a diagram of the main data model:
hledger parses the journal file into a Journal, which contains a list of Transactions, each containing multiple Postings of some MixedAmount (multiple single-Commodity Amounts) to some AccountName. Commands get and render Reports from the Journal, or sometimes from a Ledger, which contains Accounts representing the summed balances and other details of each account.
After surveying the packages, modules, and data types, try tracing the execution of a hledger command:
- CLI stuff is in hledger:Hledger.Cli.
- hledger:Hledger.Cli.Main:main parses the command line to select a command, then
- gives it to hledger:Hledger.Cli.Utils:withJournalDo, which runs it after doing all the initial parsing.
- Parsing code is under hledger-lib:Hledger.Read, eg the hledger-lib:Hledger.Read.JournalReader.
- Commands extract useful information from the parsed data model using hledger-lib:Hledger.Reports, and
- render it to the console.
- Everything uses the types and data utilities under hledger-lib:Hledger.Data, and the general helpers from hledger-lib:Hledger.Utils and below.
hledger-web
hledger-web is in a third cabal package:
hledger-web - web interface (github)
It is a single-executable web application using the yesod framework. It runs a built-in web server serving some views of the journal file, reading it at startup and again whenever it changes. It can also append new transactions to the journal file. There are two main views, which can be filtered with query arguments:
-
/journal, showing general journal entries (like
hledger print
) -
/register, showing transactions affecting an account (slightly different from
hledger register
, which shows postings).
There is also:
- a sidebar (toggled by pressing
s
) showing the chart of accounts (likehledger balance
) - an add form 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
- templates/default-layout-wrapper.hamlet
- Foundation
- Handler.*
- static/hledger.js
- 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.
The quickest way to test changes is make ghciweb
, :main --serve
, control-C, :r
, repeat.
No linking is required, and changes to static files like hledger.js are visible after reloading a page.
Another way is yesod devel
, which rebuilds automatically when files
change, including config files, templates and static files (but only in the hledger-web package).
A third way is make autoweb
, if you can get it working (see the
makefile for instructions). This rebuilds automatically when haskell
files change in any of the hledger{-lib,,-web} packages.