Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions haskell/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,27 @@ runtime dependencies** — no Cabal/Stack packages, no `aeson`, no `regex-*`. Th
test runner ships a hand-written JSON reader; the library vendors a small
RE2-subset regex engine (`src/Vregex.hs`).

## Releasing to Hackage

The package is `voxgig-struct` (`voxgig-struct.cabal`); the version lives in
`VERSION` and is mirrored in the `.cabal` (the `publish` targets guard that the
two match). **Hackage uploads are permanent** — a version can be deprecated but
never changed or removed — so the workflow is candidate-first:

```
make publish-candidate # changeable/removable candidate; verify the page + Haddock
make publish # permanent upload + git tag haskell/vX.Y.Z
```

Both need `HACKAGE_TOKEN` (account → tokens); `publish` also needs a token to
push the tag. With the aql dry-run filler token in the env, both targets no-op
loudly instead of touching the network.

Dependency bounds follow PVP — **lower AND upper on every unique dependency**,
declared once (`base >=4.14 && <5`, `array >=0.5 && <0.6` in the `library`
stanza; there is no separate `test-suite` stanza to duplicate them into).
Re-run `cabal gen-bounds` if the dependency set ever changes.

## The value model

The canonical algorithm mutates nodes in place and relies on reference-stable
Expand Down
24 changes: 22 additions & 2 deletions haskell/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Requires GHC (`ghc` / `runghc`) on PATH. No third-party dependencies
# (only the GHC boot libraries: base, array, etc.).

.PHONY: test lint build inspect clean reset publish format
.PHONY: test lint build inspect clean reset publish publish-candidate format

BUILDDIR := .hsbuild

Expand Down Expand Up @@ -33,7 +33,10 @@ reset: clean

# Release: build the source distribution and publish to Hackage (cabal upload
# --publish), then tag haskell/vX.Y.Z. NB Hackage releases are permanent (a
# version can be deprecated but never deleted). The .cabal version must match
# version can be deprecated but never deleted). ALWAYS dry-run the real thing
# with `make publish-candidate` first: a candidate is changeable/removable, so
# use it to confirm the package page renders, the sdist builds, and Haddock is
# clean before committing to the permanent index. The .cabal version must match
# VERSION (guarded below) since the sdist tarball name comes from the .cabal.
# Auth (from the aql vault at release time, none persisted on disk):
# HACKAGE_TOKEN Hackage API token (hackage.haskell.org -> account -> tokens)
Expand All @@ -43,6 +46,23 @@ VERSION := $(shell cat VERSION)
TAG := haskell/v$(VERSION)
SDIST := dist-newstyle/sdist/voxgig-struct-$(VERSION).tar.gz
AQL_DRY_RUN_FILLER := AQL-DRY-RUN-FILLER-NOT-A-REAL-SECRET

# Upload a *candidate* (non-permanent, changeable, removable) so you can verify
# the package page, dependency bounds, and Haddock rendering before the one-way
# `make publish`. Lives at hackage.haskell.org/package/voxgig-struct-X.Y.Z/candidate.
# No git tag is created for a candidate. Only HACKAGE_TOKEN is needed.
publish-candidate: test
@cabalver=$$(sed -nE 's/^version:[[:space:]]*([0-9.]+).*/\1/p' voxgig-struct.cabal); \
[ "$$cabalver" = "$(VERSION)" ] || { echo "version mismatch: VERSION=$(VERSION) vs .cabal=$$cabalver — bump both"; exit 1; }
@set -e; \
if [ "$$HACKAGE_TOKEN" = "$(AQL_DRY_RUN_FILLER)" ] || [ "$${GITHUB_TOKEN:-$$GH_TOKEN}" = "$(AQL_DRY_RUN_FILLER)" ]; then \
echo "[dry-run] aql filler token detected: would upload candidate voxgig-struct-$(VERSION); nothing uploaded."; \
exit 0; \
fi; \
cabal sdist; \
cabal upload --token="$$HACKAGE_TOKEN" "$(SDIST)"; \
echo "Uploaded candidate: https://hackage.haskell.org/package/voxgig-struct-$(VERSION)/candidate"

publish: test
@if git rev-parse -q --verify "refs/tags/$(TAG)" >/dev/null; then \
echo "tag $(TAG) already exists — bump haskell/VERSION first"; exit 1; fi
Expand Down
Loading
Loading