From cc6655134e155dd0879ecb8eb009271e17488834 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Mon, 17 Aug 2026 16:19:02 +0200 Subject: feat(cli): --version, a CHANGELOG, and a release target Groundwork for tagging. The project had no version anywhere: not in dune-project, not in the binary, no CHANGELOG, no tags. The version lives in two places -- dune-project, which generates colitur.opam, and a constant in bin/main.ml, which is what --version prints. Two rather than one because dune's watermarking only substitutes in a release tarball, so a binary built the ordinary way from a checkout would report a placeholder. The release target rewrites both and then re-checks both, and finally requires the freshly built binary to report the version it just wrote: a release that bumped one and not the other would ship a binary disagreeing with its own package metadata. --version is deliberately not embedded in the help text. cli.t pins help's first line, and a version there would mean editing a test expectation every release for no gain. The release target mirrors lectio's, refusals included: no release from a dirty tree, none without a CHANGELOG entry for that version, none whose version bump silently failed to apply, and `make check` -- the full 1583-9999 sweep, not the sampling suite -- must pass before the tag is created. --- CHANGELOG.md | 6 ++++++ Makefile | 26 +++++++++++++++++++++++++- bin/main.ml | 15 +++++++++++++++ colitur.opam | 1 + dune-project | 1 + test/cli.t | 18 ++++++++++++++++++ 6 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1c5d1fb --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog + +One line per release. Versions are git tags (`vX.Y.Z`). + +## [0.1.0] - 2026-08-17 +- First tag. The Extraordinary Form (1962 Roman Missal) end to end: the complete temporal cycle, the resolution engine (occurrence, precedence, commemorations, transfers) against the sanctoral calendar, and the Mass reading citations for every day — correct and total over 1583–9999, with no clock, randomness or environment read below the CLI. Four commands (`easter`, `temporal`, `day`, `readings`) plus `--help`/`--version`, a man page, and a Makefile that installs binary, calendar data and man page into `~/.local` by default. Validated in five layers: types, QCheck properties over every year in the domain, a 16 801-day differential against the sibling lectio engine, two independent missalemeum oracle windows (2026–2027 and 2038), and ~36 hand-verified golden pins — 375 tests. Rules are computed against the Missal and its *Rubricae Generales*, with every published-calendar divergence adjudicated against the primary text rather than adopted; several are resolved in this engine's favour and cited in the register. Not included: the Ordinary Form, the chants (Psalm/Gradual/Tract/Alleluia/Sequence, deliberately rejected rather than guessed), and template-driven output. diff --git a/Makefile b/Makefile index 75372ac..f90d32e 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ MANDIR := $(PREFIX)/share/man/man1 # over documenting the dune commands. DUNE := opam exec -- -.PHONY: help build test check install uninstall reinstall clean fmt man doc +.PHONY: help build test check install uninstall reinstall clean fmt man doc release help: ## show this help @grep -hE '^[a-z-]+:.*##' $(MAKEFILE_LIST) | sed -E 's/:.*## /\t/' | sort @@ -53,3 +53,27 @@ fmt: ## format the OCaml sources clean: ## remove build artifacts $(DUNE) dune clean + +# Mirrors lectio's own release target, including its refusals: no release from +# a dirty tree, no release without a CHANGELOG line, and no release whose +# version bump silently failed to apply. The version lives in TWO places +# (dune-project, which feeds colitur.opam, and bin/main.ml's own constant, +# which is what --version prints), so both are rewritten and both are +# re-checked -- a release that bumped one and not the other would ship a +# binary disagreeing with its own package metadata. +release: ## cut a release: make release VERSION=0.1.0 (add its CHANGELOG line first) + @test -n "$(VERSION)" || { echo "set VERSION=X.Y.Z" >&2; exit 2; } + @echo "$(VERSION)" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$$' || { echo "VERSION must be X.Y.Z" >&2; exit 2; } + @test -z "$$(git status --porcelain)" || { echo "working tree not clean; commit or stash first" >&2; exit 2; } + @grep -q '## \[$(VERSION)\]' CHANGELOG.md || { echo "add a one-line CHANGELOG.md entry under '## [$(VERSION)]' first" >&2; exit 2; } + @sed -i 's/^(version .*)$$/(version $(VERSION))/' dune-project + @sed -i 's/^let version = ".*"/let version = "$(VERSION)"/' bin/main.ml + @grep -q '^(version $(VERSION))$$' dune-project || { echo "dune-project version bump failed" >&2; exit 2; } + @grep -q '^let version = "$(VERSION)"' bin/main.ml || { echo "bin/main.ml version bump failed" >&2; exit 2; } + $(MAKE) check + @test "$$($(DUNE) dune exec --no-build colitur -- --version)" = "$(VERSION)" || \ + { echo "the built binary does not report $(VERSION)" >&2; exit 2; } + git add dune-project colitur.opam bin/main.ml CHANGELOG.md + git commit -m "release: v$(VERSION)" + git tag -a "v$(VERSION)" -m "colitur $(VERSION)" + @echo "tagged v$(VERSION). publish with: git push origin main v$(VERSION)" diff --git a/bin/main.ml b/bin/main.ml index 573f190..141f016 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -336,6 +336,17 @@ let readings_report y = resolved_year_report ~line:readings_line y pager or grepped); being invoked wrongly is an error, so [usage] prints a one-liner to stderr and exits 2, keeping stdout clean for whatever the caller was really trying to capture. *) +(* Kept in lockstep with dune-project's own [(version ...)] by `make release`, + which bumps BOTH and refuses to proceed if either edit did not take. Two + places rather than one because dune's watermarking (`dune subst`) only + substitutes in a release tarball, not in a plain `dune build` from a + checkout, so a binary built the ordinary way would report a placeholder. + A constant edited by the release target is what lectio does too, for the + same reason. Deliberately NOT embedded in [help_text]: the cram test pins + help's first line, and a version in it would make every release edit a + test expectation for no gain. *) +let version = "0.1.0" + let help_text = {|colitur -- deterministic liturgical calendar engine (Roman rite, 1962) @@ -345,6 +356,7 @@ usage: colitur day the resolved day identity, one line per day colitur readings the Mass reading citations, one line per day colitur -h, --help this help + colitur -V, --version print the version and exit is a civil year, 1583..9999 inclusive. Each report covers 1 January to 31 December of that year, not a liturgical year. @@ -394,6 +406,9 @@ let with_year ys f = let () = match Sys.argv with | [| _; ("-h" | "--help" | "help") |] -> print_help () + | [| _; ("-V" | "--version" | "version") |] -> + print_endline version; + exit 0 | [| _; "easter"; ys |] -> with_year ys easter_report | [| _; "temporal"; ys |] -> with_year ys temporal_report | [| _; "day"; ys |] -> with_year ys day_report diff --git a/colitur.opam b/colitur.opam index 3edcfa4..12d0e2b 100644 --- a/colitur.opam +++ b/colitur.opam @@ -1,5 +1,6 @@ # This file is generated by dune, edit dune-project instead opam-version: "2.0" +version: "0.1.0" synopsis: "Deterministic liturgical calendar engine (computus liturgicus)" depends: [ "ocaml" diff --git a/dune-project b/dune-project index 91824f4..ccef6df 100644 --- a/dune-project +++ b/dune-project @@ -1,5 +1,6 @@ (lang dune 3.0) (name colitur) +(version 0.1.0) (generate_opam_files true) (cram enable) diff --git a/test/cli.t b/test/cli.t index d7e86f7..419e7f0 100644 --- a/test/cli.t +++ b/test/cli.t @@ -241,3 +241,21 @@ Every command the help lists is a command the binary actually accepts. This is the check that catches help text drifting away from the dispatch: $ for c in easter temporal day readings; do colitur $c 2026 > /dev/null || echo "$c FAILED"; done + +--version prints the version alone, to standard output, exit 0. Deliberately +not embedded in the help text above: this pin would then have to be edited +every release, for no gain. The Makefile's release target asserts the built +binary reports the version it just wrote, so the two cannot drift. + + $ colitur --version + 0.1.0 + + $ colitur -V + 0.1.0 + +Agreement with the package metadata (dune-project's own (version ...), which +generates colitur.opam) is NOT asserted here: the cram sandbox has no source +tree to read dune-project from. It is asserted in the Makefile's release +target instead, which rewrites both and then requires the freshly built +binary to report the version it just wrote -- the point in time where a drift +between the two could actually be introduced. -- cgit v1.3