mirror of
https://github.com/simonmichael/hledger.git
synced 2024-12-26 03:42:25 +03:00
;doc: move more content out of CONTRIBUTING
This commit is contained in:
parent
0fc4181456
commit
d62d302fdc
152
BENCHMARKS.md
Normal file
152
BENCHMARKS.md
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
# 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).
|
||||||
|
|
||||||
|
|
||||||
|
|
211
CODE.md
Normal file
211
CODE.md
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
# Code
|
||||||
|
|
||||||
|
hledger is a suite of applications, tools and libraries.
|
||||||
|
The main hledger code repository is [github.com/simonmichael/hledger](https://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](https://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](https://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](https://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:Journal),
|
||||||
|
which contains a list of
|
||||||
|
[Transactions](https://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:Transaction),
|
||||||
|
each containing multiple
|
||||||
|
[Postings](https://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:Posting)
|
||||||
|
of some
|
||||||
|
[MixedAmount](https://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:MixedAmount)
|
||||||
|
(multiple
|
||||||
|
single-[CommoditySymbol](https://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:CommoditySymbol)
|
||||||
|
[Amounts](https://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:Amount))
|
||||||
|
to some
|
||||||
|
[AccountName](https://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:AccountName).
|
||||||
|
When needed, the Journal is further processed to derive a
|
||||||
|
[Ledger](https://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:Ledger),
|
||||||
|
which contains summed
|
||||||
|
[Accounts](https://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:Account).
|
||||||
|
In [Hledger.Reports](https://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:
|
||||||
|
|
||||||
|
<a href="https://hledger.org/images/data-model.png" class="highslide" onclick="return hs.expand(this)">
|
||||||
|
<img src="https://hledger.org/images/data-model.png" alt="diagram" title="main data types" style="max-width:100%;">
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- generated by plantuml from:
|
||||||
|
<uml>
|
||||||
|
hide empty members
|
||||||
|
hide circle
|
||||||
|
skinparam packageStyle Rect
|
||||||
|
|
||||||
|
Ledger *-- Journal
|
||||||
|
Ledger *-- "*" Account
|
||||||
|
note top of Ledger: A Journal and all its accounts with their balances.\nUsed for balance report
|
||||||
|
note top of Journal: A journal file and parsed transactions & directives.\nUsed for print & register reports
|
||||||
|
note bottom of Account: An account's name, balance (inclusive &\nexclusive), parent and child accounts
|
||||||
|
Account o-- "*" Account :subaccounts, parent
|
||||||
|
Journal o-- File
|
||||||
|
File o-- "*" File :include
|
||||||
|
Journal *-- "*" MarketPrice
|
||||||
|
Journal *-- "*" Transaction
|
||||||
|
MarketPrice -- Date
|
||||||
|
MarketPrice -- Amount
|
||||||
|
Transaction -- Date
|
||||||
|
Transaction *-- "*" Posting
|
||||||
|
Transaction o-- "*" Tag
|
||||||
|
Posting o- "*" Tag
|
||||||
|
Posting -- "0..1" Date
|
||||||
|
Account -- AccountName
|
||||||
|
Posting -- AccountName
|
||||||
|
Account -- "2" MixedAmount
|
||||||
|
Posting -- MixedAmount
|
||||||
|
MixedAmount *-- "*" Amount
|
||||||
|
Amount -- CommoditySymbol
|
||||||
|
Amount -- Quantity
|
||||||
|
Amount -- AmountPrice
|
||||||
|
Amount -- AmountStyle
|
||||||
|
</uml>
|
||||||
|
-->
|
||||||
|
|
||||||
|
### hledger
|
||||||
|
|
||||||
|
[package](https://hackage.haskell.org/package/hledger),
|
||||||
|
[code](https://github.com/simonmichael/hledger/tree/master/hledger),
|
||||||
|
[manual](https://hledger.org/hledger.html)
|
||||||
|
|
||||||
|
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](https://hackage.haskell.org/package/hledger-ui),
|
||||||
|
[code](https://github.com/simonmichael/hledger/tree/master/hledger-ui),
|
||||||
|
[manual](https://hledger.org/hledger-ui.html)
|
||||||
|
|
||||||
|
A terminal interface.
|
||||||
|
|
||||||
|
### hledger-web
|
||||||
|
|
||||||
|
[package](https://hackage.haskell.org/package/hledger-web),
|
||||||
|
[code](https://github.com/simonmichael/hledger/tree/master/hledger-web),
|
||||||
|
[manual](https://hledger.org/hledger-web.html)
|
||||||
|
|
||||||
|
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](https://hledger.org/hledger.html#queries):
|
||||||
|
|
||||||
|
- [/journal](https://demo.hledger.org/journal), showing general journal entries (like `hledger print`)
|
||||||
|
|
||||||
|
- [/register](https://demo.hledger.org/register?q=inacct:Expenses:Food),
|
||||||
|
showing transactions affecting an account (slightly different from
|
||||||
|
hledger's [register](https://hledger.org/hledger.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](https://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.)
|
||||||
|
|
||||||
|
## 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](https://thread.gmane.org/gmane.comp.finance.ledger.hledger/1070)<!-- missing ,
|
||||||
|
[log](https://hledger.org/static/irc-20140725-code-review.html) -->
|
||||||
|
- Dev sprint/party 2015/10/10:
|
||||||
|
[discussion](https://thread.gmane.org/gmane.comp.finance.ledger.hledger/1254)<!-- ircbrowse down ,
|
||||||
|
[pre-chat](https://ircbrowse.net/day/hledger/2015/10/10),
|
||||||
|
[log](https://ircbrowse.net/day/hledger/2015/10/11) -->
|
||||||
|
|
||||||
|
|
885
CONTRIBUTING.md
885
CONTRIBUTING.md
@ -7,6 +7,9 @@
|
|||||||
This is a collection of old developer docs on all topics.
|
This is a collection of old developer docs on all topics.
|
||||||
Gradually, these are moving to separate files/pages
|
Gradually, these are moving to separate files/pages
|
||||||
and this doc is becoming a focussed guide for new contributors.
|
and this doc is becoming a focussed guide for new contributors.
|
||||||
|
If you are unexpectedly seeing this page after following a link,
|
||||||
|
the content probably moved to a separate page:
|
||||||
|
see the [Developer docs](dev.md).
|
||||||
|
|
||||||
## Quick Links
|
## Quick Links
|
||||||
|
|
||||||
@ -243,885 +246,3 @@ See [Developer workflows](#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](https://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](#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
|
|
||||||
|
|
||||||
|
|
||||||
## Code
|
|
||||||
|
|
||||||
hledger is a suite of applications, tools and libraries.
|
|
||||||
The main hledger code repository is [github.com/simonmichael/hledger](https://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](https://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](https://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](https://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:Journal),
|
|
||||||
which contains a list of
|
|
||||||
[Transactions](https://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:Transaction),
|
|
||||||
each containing multiple
|
|
||||||
[Postings](https://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:Posting)
|
|
||||||
of some
|
|
||||||
[MixedAmount](https://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:MixedAmount)
|
|
||||||
(multiple
|
|
||||||
single-[CommoditySymbol](https://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:CommoditySymbol)
|
|
||||||
[Amounts](https://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:Amount))
|
|
||||||
to some
|
|
||||||
[AccountName](https://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:AccountName).
|
|
||||||
When needed, the Journal is further processed to derive a
|
|
||||||
[Ledger](https://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:Ledger),
|
|
||||||
which contains summed
|
|
||||||
[Accounts](https://hackage.haskell.org/package/hledger-lib/docs/Hledger-Data-Types.html#t:Account).
|
|
||||||
In [Hledger.Reports](https://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:
|
|
||||||
|
|
||||||
<a href="https://hledger.org/images/data-model.png" class="highslide" onclick="return hs.expand(this)">
|
|
||||||
<img src="https://hledger.org/images/data-model.png" alt="diagram" title="main data types" style="max-width:100%;">
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<!-- generated by plantuml from:
|
|
||||||
<uml>
|
|
||||||
hide empty members
|
|
||||||
hide circle
|
|
||||||
skinparam packageStyle Rect
|
|
||||||
|
|
||||||
Ledger *-- Journal
|
|
||||||
Ledger *-- "*" Account
|
|
||||||
note top of Ledger: A Journal and all its accounts with their balances.\nUsed for balance report
|
|
||||||
note top of Journal: A journal file and parsed transactions & directives.\nUsed for print & register reports
|
|
||||||
note bottom of Account: An account's name, balance (inclusive &\nexclusive), parent and child accounts
|
|
||||||
Account o-- "*" Account :subaccounts, parent
|
|
||||||
Journal o-- File
|
|
||||||
File o-- "*" File :include
|
|
||||||
Journal *-- "*" MarketPrice
|
|
||||||
Journal *-- "*" Transaction
|
|
||||||
MarketPrice -- Date
|
|
||||||
MarketPrice -- Amount
|
|
||||||
Transaction -- Date
|
|
||||||
Transaction *-- "*" Posting
|
|
||||||
Transaction o-- "*" Tag
|
|
||||||
Posting o- "*" Tag
|
|
||||||
Posting -- "0..1" Date
|
|
||||||
Account -- AccountName
|
|
||||||
Posting -- AccountName
|
|
||||||
Account -- "2" MixedAmount
|
|
||||||
Posting -- MixedAmount
|
|
||||||
MixedAmount *-- "*" Amount
|
|
||||||
Amount -- CommoditySymbol
|
|
||||||
Amount -- Quantity
|
|
||||||
Amount -- AmountPrice
|
|
||||||
Amount -- AmountStyle
|
|
||||||
</uml>
|
|
||||||
-->
|
|
||||||
|
|
||||||
#### hledger
|
|
||||||
|
|
||||||
[package](https://hackage.haskell.org/package/hledger),
|
|
||||||
[code](https://github.com/simonmichael/hledger/tree/master/hledger),
|
|
||||||
[manual](https://hledger.org/hledger.html)
|
|
||||||
|
|
||||||
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](https://hackage.haskell.org/package/hledger-ui),
|
|
||||||
[code](https://github.com/simonmichael/hledger/tree/master/hledger-ui),
|
|
||||||
[manual](https://hledger.org/hledger-ui.html)
|
|
||||||
|
|
||||||
A terminal interface.
|
|
||||||
|
|
||||||
#### hledger-web
|
|
||||||
|
|
||||||
[package](https://hackage.haskell.org/package/hledger-web),
|
|
||||||
[code](https://github.com/simonmichael/hledger/tree/master/hledger-web),
|
|
||||||
[manual](https://hledger.org/hledger-web.html)
|
|
||||||
|
|
||||||
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](https://hledger.org/hledger.html#queries):
|
|
||||||
|
|
||||||
- [/journal](https://demo.hledger.org/journal), showing general journal entries (like `hledger print`)
|
|
||||||
|
|
||||||
- [/register](https://demo.hledger.org/register?q=inacct:Expenses:Food),
|
|
||||||
showing transactions affecting an account (slightly different from
|
|
||||||
hledger's [register](https://hledger.org/hledger.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](https://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.)
|
|
||||||
|
|
||||||
### 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](https://thread.gmane.org/gmane.comp.finance.ledger.hledger/1070)<!-- missing ,
|
|
||||||
[log](https://hledger.org/static/irc-20140725-code-review.html) -->
|
|
||||||
- Dev sprint/party 2015/10/10:
|
|
||||||
[discussion](https://thread.gmane.org/gmane.comp.finance.ledger.hledger/1254)<!-- ircbrowse down ,
|
|
||||||
[pre-chat](https://ircbrowse.net/day/hledger/2015/10/10),
|
|
||||||
[log](https://ircbrowse.net/day/hledger/2015/10/11) -->
|
|
||||||
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
About testing in the hledger project, as of 201809.
|
|
||||||
|
|
||||||
### Kinds of tests
|
|
||||||
|
|
||||||
<div style="margin:1em 2em; font-style:italic;">
|
|
||||||
"Here, then, is a list of properties of tests. Not all tests need to exhibit all properties. However, no property should be given up without receiving a property of greater value in return.
|
|
||||||
|
|
||||||
- Isolated — tests should return the same results regardless of the order in which they are run.
|
|
||||||
- Composable — if tests are isolated, then I can run 1 or 10 or 100 or 1,000,000 and get the same results.
|
|
||||||
- Fast — tests should run quickly.
|
|
||||||
- Inspiring — passing the tests should inspire confidence
|
|
||||||
- Writable — tests should be cheap to write relative to the cost of the code being tested.
|
|
||||||
- Readable — tests should be comprehensible for reader, invoking the motivation for writing this particular test.
|
|
||||||
- Behavioral — tests should be sensitive to changes in the behavior of the code under test. If the behavior changes, the test result should change.
|
|
||||||
- Structure-insensitive — tests should not change their result if the structure of the code changes.
|
|
||||||
- Automated — tests should run without human intervention.
|
|
||||||
- Specific — if a test fails, the cause of the failure should be obvious.
|
|
||||||
- Deterministic — if nothing changes, the test result shouldn’t change.
|
|
||||||
- Predictive — if the tests all pass, then the code under test should be suitable for production."
|
|
||||||
--[Kent Beck](https://medium.com/@kentbeck_7670/test-desiderata-94150638a4b3)
|
|
||||||
</div>
|
|
||||||
|
|
||||||
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 the
|
|
||||||
[tasty](https://hackage.haskell.org/package/tasty) test runner,
|
|
||||||
[tasty-hunit](https://hackage.haskell.org/package/tasty-hunit) HUnit-style tests,
|
|
||||||
and some helpers from
|
|
||||||
[Hledger.Utils.Test](https://github.com/simonmichael/hledger/blob/master/hledger-lib/Hledger/Utils/Test.hs),
|
|
||||||
such as:
|
|
||||||
|
|
||||||
- `tests` and `test` aliases for `testGroup` and `testCase`
|
|
||||||
- `assert*` helpers for constructing various kinds of assertions
|
|
||||||
|
|
||||||
We would like our unit tests to be:
|
|
||||||
|
|
||||||
- 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 current 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
|
|
||||||
|
|
||||||
-- a group of several named tests for functionA
|
|
||||||
tests "functionA" [
|
|
||||||
test "a basic test" $ assertBool "" SOMEBOOL
|
|
||||||
,test "a pretty equality test" $ SOMEEXPR @?= EXPECTEDVALUE
|
|
||||||
,test "a pretty parsing test" $ assertParseEq PARSER INPUT EXPECTEDRESULT
|
|
||||||
,test "a multiple assertions test" $ do
|
|
||||||
A @?= B
|
|
||||||
doSomeIO
|
|
||||||
C @?= D
|
|
||||||
]
|
|
||||||
|
|
||||||
-- a single test containing multiple unnamed assertions for functionB
|
|
||||||
,test "functionB" $ do
|
|
||||||
assertBool "" BOOL
|
|
||||||
EXPR @?= VALUE
|
|
||||||
|
|
||||||
,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](https://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-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](https://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](https://hackage.haskell.org/package/shelltestrunner).
|
|
||||||
Tests are defined in files named `*.test` under
|
|
||||||
[hledger/test/](https://github.com/simonmichael/hledger/tree/master/hledger/test),
|
|
||||||
grouped by *component* (command or topic name).
|
|
||||||
For more about these, see the README there.
|
|
||||||
|
|
||||||
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
|
|
||||||
------------- ------------ ---------------------------------------------------------------
|
|
||||||
|
|
||||||
### 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
|
|
||||||
------------- ------ ----- ------------
|
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Version numbers
|
|
||||||
|
|
||||||
Some places version numbers appear:
|
|
||||||
|
|
||||||
- --version (and sometimes --help) output of all hledger* executables
|
|
||||||
- web manuals on hledger.org
|
|
||||||
- download page
|
|
||||||
- changelogs
|
|
||||||
- release notes
|
|
||||||
- release announcements
|
|
||||||
- hackage/stackage uris
|
|
||||||
- cabal tarball filenames
|
|
||||||
- platform-specific packages
|
|
||||||
|
|
||||||
Some old version numbering goals:
|
|
||||||
|
|
||||||
1. automation, robustness, simplicity, platform independence
|
|
||||||
2. cabal versions must be all-numeric
|
|
||||||
3. release versions can be concise (without extra .0's)
|
|
||||||
4. releases should have a corresponding VCS tag
|
|
||||||
5. development builds should have a precise version appearing in --version
|
|
||||||
6. development builds should generate cabal packages with non-confusing versions
|
|
||||||
7. there should be a way to mark builds/releases as alpha or beta
|
|
||||||
8. avoid unnecessary compiling and linking
|
|
||||||
9. minimise VCS noise and syncing issues (commits, unrecorded changes)
|
|
||||||
|
|
||||||
Current version numbering policy:
|
|
||||||
|
|
||||||
- We (should) follow <https://haskell.org/haskellwiki/Package_versioning_policy>
|
|
||||||
|
|
||||||
- The "full release version" is ma.jor.minor, where minor is 0 for a
|
|
||||||
normal release or 1..n for bugfix releases. Each component is a
|
|
||||||
natural number (can be >= 10). Eg: 1.13 major release, 1.13.1
|
|
||||||
bugfix release.
|
|
||||||
|
|
||||||
- The "release version", which we prefer to use when possible, is
|
|
||||||
just ma.jor when minor is 0. Ie elide the dot zero.
|
|
||||||
|
|
||||||
- The build version is ma.jor.minor+patches, where patches is the number
|
|
||||||
of patches applied in the current repo since the last release tag.
|
|
||||||
|
|
||||||
- `hledger --version` shows the release version or build version as
|
|
||||||
appropriate.
|
|
||||||
|
|
||||||
- Release tags in the VCS are like PKG-VERSION. Eg hledger-1.13,
|
|
||||||
- hledger-ui-1.13.1.
|
|
||||||
|
|
||||||
Current process:
|
|
||||||
|
|
||||||
- In each hledger package directory there's a `.version` file
|
|
||||||
containing its desired version number.
|
|
||||||
|
|
||||||
- After changing a `.version` file: run `./Shake setversion` to
|
|
||||||
propagate the versions to all other places in the packages where
|
|
||||||
they should appear. This is not perfect (see Shake.hs) so review and
|
|
||||||
manually adjust the proposed changes before committing. Those
|
|
||||||
places include (you can also run these rules individually):
|
|
||||||
|
|
||||||
- `PKG/package.yaml` contains the cabal package version declaration,
|
|
||||||
bounds on other hledger packages, and a CPP VERSION macro used in
|
|
||||||
`hledger/Hledger/Cli/Version.hs`. Changes in package.yaml will be
|
|
||||||
propagated to `PKG/PKG.cabal` on the next stack or Shake build, or
|
|
||||||
by `make gencabal`.
|
|
||||||
|
|
||||||
- `PKG/.version.m4` contains the _version_ macro used in documentation source files (*.m4.md). It is updated by `./Shake setversion`.
|
|
||||||
|
|
||||||
- `PKG/.date.m4` contains the _monthyear_ macro used in man pages. It is updated by `./Shake manuals`.
|
|
||||||
|
|
||||||
- At release time:
|
|
||||||
|
|
||||||
- `./Shake PKG/CHANGES.md-finalise` converts the topmost heading, if
|
|
||||||
it is an interim heading (just a commit hash), to a permanent
|
|
||||||
heading containing the version and today's date.
|
|
||||||
|
|
||||||
- for each package being released, a PKG-VERSION git tag is created.
|
|
||||||
|
|
||||||
- At major release time:
|
|
||||||
|
|
||||||
- A new snapshot of the reference docs is added to the website, by
|
|
||||||
`./Shake site/doc/VERSION/.snapshot`, and added to the links in
|
|
||||||
`site/js/site.js`.
|
|
||||||
|
|
||||||
## 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
|
|
||||||
|
|
||||||
### Four kinds of documentation
|
|
||||||
|
|
||||||
20191209: needs update. See also doc/README.
|
|
||||||
|
|
||||||
|
|
||||||
<div style="margin:1em 2em; font-style:italic;">
|
|
||||||
"There is a secret that needs to be understood in order to write good
|
|
||||||
software documentation: there isn’t one thing called documentation,
|
|
||||||
there are four. They are: tutorials, how-to guides, explanation and
|
|
||||||
technical reference. They represent four different purposes or
|
|
||||||
functions, and require four different approaches to their creation."
|
|
||||||
--[Daniele Procida] (https://news.ycombinator.com/item?id=21289832)
|
|
||||||
</div>
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
|
|
||||||
## Commit messages
|
|
||||||
|
|
||||||
See [COMMITS](COMMITS.html).
|
|
||||||
|
|
||||||
## Issues
|
|
||||||
|
|
||||||
See [ISSUES](ISSUES.html).
|
|
||||||
|
|
||||||
## Pull Requests
|
|
||||||
|
|
||||||
See [PULLREQUESTS](PULLREQUESTS.html).
|
|
||||||
|
|
||||||
## Developer workflows
|
|
||||||
|
|
||||||
See [WORKFLOWS](WORKFLOWS.html).
|
|
||||||
|
|
||||||
|
25
DOCS.md
Normal file
25
DOCS.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Docs
|
||||||
|
|
||||||
|
20191209: needs update. See also doc/README.
|
||||||
|
|
||||||
|
## Four kinds of documentation
|
||||||
|
|
||||||
|
<div style="margin:1em 2em; font-style:italic;">
|
||||||
|
"There is a secret that needs to be understood in order to write good
|
||||||
|
software documentation: there isn’t one thing called documentation,
|
||||||
|
there are four. They are: tutorials, how-to guides, explanation and
|
||||||
|
technical reference. They represent four different purposes or
|
||||||
|
functions, and require four different approaches to their creation."
|
||||||
|
--[Daniele Procida] (https://news.ycombinator.com/item?id=21289832)
|
||||||
|
</div>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
31
EXAMPLES.md
Normal file
31
EXAMPLES.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
## Collected examples
|
||||||
|
|
||||||
|
Many example input files in journal and other formats can be found
|
||||||
|
in `examples/` in the `hledger` repo.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
```
|
||||||
|
|
||||||
|
|
49
FUNDING.md
Normal file
49
FUNDING.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# 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.
|
||||||
|
|
40
MAKE.md
Normal file
40
MAKE.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# 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](https://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](#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
|
||||||
|
```
|
||||||
|
|
||||||
|
|
13
SHAKE.md
Normal file
13
SHAKE.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
|
269
TESTS.md
Normal file
269
TESTS.md
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
# Tests
|
||||||
|
|
||||||
|
About testing in the hledger project, as of 201809.
|
||||||
|
|
||||||
|
## Kinds of tests
|
||||||
|
|
||||||
|
<div style="margin:1em 2em; font-style:italic;">
|
||||||
|
"Here, then, is a list of properties of tests. Not all tests need to exhibit all properties. However, no property should be given up without receiving a property of greater value in return.
|
||||||
|
|
||||||
|
- Isolated — tests should return the same results regardless of the order in which they are run.
|
||||||
|
- Composable — if tests are isolated, then I can run 1 or 10 or 100 or 1,000,000 and get the same results.
|
||||||
|
- Fast — tests should run quickly.
|
||||||
|
- Inspiring — passing the tests should inspire confidence
|
||||||
|
- Writable — tests should be cheap to write relative to the cost of the code being tested.
|
||||||
|
- Readable — tests should be comprehensible for reader, invoking the motivation for writing this particular test.
|
||||||
|
- Behavioral — tests should be sensitive to changes in the behavior of the code under test. If the behavior changes, the test result should change.
|
||||||
|
- Structure-insensitive — tests should not change their result if the structure of the code changes.
|
||||||
|
- Automated — tests should run without human intervention.
|
||||||
|
- Specific — if a test fails, the cause of the failure should be obvious.
|
||||||
|
- Deterministic — if nothing changes, the test result shouldn’t change.
|
||||||
|
- Predictive — if the tests all pass, then the code under test should be suitable for production."
|
||||||
|
--[Kent Beck](https://medium.com/@kentbeck_7670/test-desiderata-94150638a4b3)
|
||||||
|
</div>
|
||||||
|
|
||||||
|
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 the
|
||||||
|
[tasty](https://hackage.haskell.org/package/tasty) test runner,
|
||||||
|
[tasty-hunit](https://hackage.haskell.org/package/tasty-hunit) HUnit-style tests,
|
||||||
|
and some helpers from
|
||||||
|
[Hledger.Utils.Test](https://github.com/simonmichael/hledger/blob/master/hledger-lib/Hledger/Utils/Test.hs),
|
||||||
|
such as:
|
||||||
|
|
||||||
|
- `tests` and `test` aliases for `testGroup` and `testCase`
|
||||||
|
- `assert*` helpers for constructing various kinds of assertions
|
||||||
|
|
||||||
|
We would like our unit tests to be:
|
||||||
|
|
||||||
|
- 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 current 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
|
||||||
|
|
||||||
|
-- a group of several named tests for functionA
|
||||||
|
tests "functionA" [
|
||||||
|
test "a basic test" $ assertBool "" SOMEBOOL
|
||||||
|
,test "a pretty equality test" $ SOMEEXPR @?= EXPECTEDVALUE
|
||||||
|
,test "a pretty parsing test" $ assertParseEq PARSER INPUT EXPECTEDRESULT
|
||||||
|
,test "a multiple assertions test" $ do
|
||||||
|
A @?= B
|
||||||
|
doSomeIO
|
||||||
|
C @?= D
|
||||||
|
]
|
||||||
|
|
||||||
|
-- a single test containing multiple unnamed assertions for functionB
|
||||||
|
,test "functionB" $ do
|
||||||
|
assertBool "" BOOL
|
||||||
|
EXPR @?= VALUE
|
||||||
|
|
||||||
|
,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](https://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-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](https://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](https://hackage.haskell.org/package/shelltestrunner).
|
||||||
|
Tests are defined in files named `*.test` under
|
||||||
|
[hledger/test/](https://github.com/simonmichael/hledger/tree/master/hledger/test),
|
||||||
|
grouped by *component* (command or topic name).
|
||||||
|
For more about these, see the README there.
|
||||||
|
|
||||||
|
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
|
||||||
|
------------- ------------ ---------------------------------------------------------------
|
||||||
|
|
||||||
|
## 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
|
||||||
|
------------- ------ ----- ------------
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
82
VERSIONNUMBERS.md
Normal file
82
VERSIONNUMBERS.md
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# Version numbers
|
||||||
|
|
||||||
|
Some places version numbers appear:
|
||||||
|
|
||||||
|
- --version (and sometimes --help) output of all hledger* executables
|
||||||
|
- web manuals on hledger.org
|
||||||
|
- download page
|
||||||
|
- changelogs
|
||||||
|
- release notes
|
||||||
|
- release announcements
|
||||||
|
- hackage/stackage uris
|
||||||
|
- cabal tarball filenames
|
||||||
|
- platform-specific packages
|
||||||
|
|
||||||
|
Some old version numbering goals:
|
||||||
|
|
||||||
|
1. automation, robustness, simplicity, platform independence
|
||||||
|
2. cabal versions must be all-numeric
|
||||||
|
3. release versions can be concise (without extra .0's)
|
||||||
|
4. releases should have a corresponding VCS tag
|
||||||
|
5. development builds should have a precise version appearing in --version
|
||||||
|
6. development builds should generate cabal packages with non-confusing versions
|
||||||
|
7. there should be a way to mark builds/releases as alpha or beta
|
||||||
|
8. avoid unnecessary compiling and linking
|
||||||
|
9. minimise VCS noise and syncing issues (commits, unrecorded changes)
|
||||||
|
|
||||||
|
Current version numbering policy:
|
||||||
|
|
||||||
|
- We (should) follow <https://haskell.org/haskellwiki/Package_versioning_policy>
|
||||||
|
|
||||||
|
- The "full release version" is ma.jor.minor, where minor is 0 for a
|
||||||
|
normal release or 1..n for bugfix releases. Each component is a
|
||||||
|
natural number (can be >= 10). Eg: 1.13 major release, 1.13.1
|
||||||
|
bugfix release.
|
||||||
|
|
||||||
|
- The "release version", which we prefer to use when possible, is
|
||||||
|
just ma.jor when minor is 0. Ie elide the dot zero.
|
||||||
|
|
||||||
|
- The build version is ma.jor.minor+patches, where patches is the number
|
||||||
|
of patches applied in the current repo since the last release tag.
|
||||||
|
|
||||||
|
- `hledger --version` shows the release version or build version as
|
||||||
|
appropriate.
|
||||||
|
|
||||||
|
- Release tags in the VCS are like PKG-VERSION. Eg hledger-1.13,
|
||||||
|
- hledger-ui-1.13.1.
|
||||||
|
|
||||||
|
Current process:
|
||||||
|
|
||||||
|
- In each hledger package directory there's a `.version` file
|
||||||
|
containing its desired version number.
|
||||||
|
|
||||||
|
- After changing a `.version` file: run `./Shake setversion` to
|
||||||
|
propagate the versions to all other places in the packages where
|
||||||
|
they should appear. This is not perfect (see Shake.hs) so review and
|
||||||
|
manually adjust the proposed changes before committing. Those
|
||||||
|
places include (you can also run these rules individually):
|
||||||
|
|
||||||
|
- `PKG/package.yaml` contains the cabal package version declaration,
|
||||||
|
bounds on other hledger packages, and a CPP VERSION macro used in
|
||||||
|
`hledger/Hledger/Cli/Version.hs`. Changes in package.yaml will be
|
||||||
|
propagated to `PKG/PKG.cabal` on the next stack or Shake build, or
|
||||||
|
by `make gencabal`.
|
||||||
|
|
||||||
|
- `PKG/.version.m4` contains the _version_ macro used in documentation source files (*.m4.md). It is updated by `./Shake setversion`.
|
||||||
|
|
||||||
|
- `PKG/.date.m4` contains the _monthyear_ macro used in man pages. It is updated by `./Shake manuals`.
|
||||||
|
|
||||||
|
- At release time:
|
||||||
|
|
||||||
|
- `./Shake PKG/CHANGES.md-finalise` converts the topmost heading, if
|
||||||
|
it is an interim heading (just a commit hash), to a permanent
|
||||||
|
heading containing the version and today's date.
|
||||||
|
|
||||||
|
- for each package being released, a PKG-VERSION git tag is created.
|
||||||
|
|
||||||
|
- At major release time:
|
||||||
|
|
||||||
|
- A new snapshot of the reference docs is added to the website, by
|
||||||
|
`./Shake site/doc/VERSION/.snapshot`, and added to the links in
|
||||||
|
`site/js/site.js`.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user