Release Automation¶
Releases are Cabal-owned. The package version in
amaru-treasury-tx.cabal is the product version; there is no
release-please manifest and no separate version.txt.
Main-branch planner¶
Every push to main runs the Release Planner workflow. It reads
Conventional Commit messages since the last v* tag and either:
- opens or updates
release/cabal-releasewith a Cabal version bump and a generatedCHANGELOG.mdsection; - creates the release tag if the just-merged release PR already contains a changelog section for the current Cabal version;
- exits without changes when there are no releasable commits.
The planner maps commit intent onto PVP-shaped Cabal versions:
| Commit signal | Cabal bump |
|---|---|
fix: / perf: |
fourth component, for example 0.1.1.0 to 0.1.1.1 |
feat: |
third component, for example 0.1.1.0 to 0.1.2.0 |
! or BREAKING CHANGE: |
second component, for example 0.1.1.0 to 0.2.0.0 |
For user-facing CLI changes that invalidate old operator scripts, use
a breaking Conventional Commit such as feat!: and include a
BREAKING CHANGE: footer. The release planner treats either marker as
a breaking release signal.
The planner must push branches and tags through RELEASE_BOT_SSH_KEY.
Using the default GITHUB_TOKEN for git writes would create branch and
tag events that do not trigger the normal CI and publication workflows.
One-time setup¶
Create a dedicated write deploy key and store the private half as a repository secret. This is repo-scoped and does not require a personal access token.
tmpdir="$(mktemp -d)"
ssh-keygen -t ed25519 \
-C 'amaru-release-bot@lambdasistemi/amaru-treasury-tx' \
-f "$tmpdir/amaru-release-bot" \
-N ''
gh api repos/lambdasistemi/amaru-treasury-tx/keys \
-f title='amaru-release-bot' \
-f key="$(cat "$tmpdir/amaru-release-bot.pub")" \
-F read_only=false
gh secret set RELEASE_BOT_SSH_KEY \
--repo lambdasistemi/amaru-treasury-tx \
--body "$(cat "$tmpdir/amaru-release-bot")"
rm -rf "$tmpdir"
gh secret list --repo lambdasistemi/amaru-treasury-tx \
| grep '^RELEASE_BOT_SSH_KEY'
gh api repos/lambdasistemi/amaru-treasury-tx/keys \
--jq '.[] | select(.title == "amaru-release-bot")'
The deploy key must have write access. A read-only key can fetch the repository but cannot push the release branch or release tag.
Publication¶
Merging the release PR does not publish directly. The next planner run
creates v<package-version>, and the tag push starts the Linux and
Darwin release workflows.
Before merging a release-bound PR, run:
nix develop --quiet -c just ci
That includes just smoke, which exercises the swap-wizard signer
regression and checks that the built CLI advertises
--extra-signer,--signer SCOPE|HEX.
Strings that follow the Cabal version¶
Anything user-facing that embeds the package version (e.g. the
User-Agent header sent to outbound quote providers) imports the
autogenerated Paths_amaru_treasury_tx module and renders
Data.Version.showVersion P.version. Do not hardcode the package
version string anywhere — the release planner only bumps
amaru-treasury-tx.cabal, and hardcoded copies drift silently and
ship stale on every release (as happened to the CoinGecko User-Agent
in v0.2.4.0, fixed by #96).
The library and unit-test stanzas in amaru-treasury-tx.cabal already
list Paths_amaru_treasury_tx under autogen-modules and
other-modules; new stanzas that need the version should do the same.
Local DevNet release evidence¶
For live local-node evidence, also run the opt-in devnet smoke:
nix develop --quiet -c just devnet-smoke node
nix develop --quiet -c just devnet-smoke registry-init
nix develop --quiet -c just devnet-smoke stake-reward-init
nix develop --quiet -c just devnet-smoke governance-withdrawal-init
nix develop --quiet -c just devnet-smoke disburse-submit
nix develop --quiet -c just devnet-smoke swap-ready
Record the generated runs/devnet/<timestamp>/ directory in the
release notes when this check is used. The node phase proves the
cardano-node-clients DevNet node boundary, socket magic 42, and
50-second epoch timing.
The registry-init phase proves the production-backed DevNet registry
and reference-script publication command. The stake-reward-init phase
then consumes the registry artifact and registers both the treasury
reward account and the permissions reward account needed by later
withdraw-zero witnesses. Latest branch stake/reward evidence is
runs/devnet/20260517T005034Z, setup tx
527c1b08fdfd1c41fe1237d4d5f1bc3572dee98a81b76b62257c958e50dc9cc8,
treasury reward account
b2b7201c62e43ae8e03b61c96931379ebbcdce61befc3f4e4b1f4be4 with
registered: true, and permissions reward account
f9dc1d931a3f52eaf83891f8621cbba5ba64f6faa5792f1b00c17333 with
registered: true. This is prerequisite evidence only; governance
funding and disburse are recorded by their own command-proof slices.
The governance-withdrawal-init phase copies and patches a short-epoch
genesis, runs registry-init and stake-reward-init prerequisites, then
invokes the shipped devnet governance-withdrawal-init command. The
production command submits and votes through the treasury-withdrawal
governance action, observes the reward account funded, builds the
withdrawal through the production withdraw/tx-build path, signs/submits
it, and writes the #150 handoff at
governance-withdrawal-init/materialized.json. Latest branch evidence
is runs/devnet/20260516T231003Z: proposal tx
baffa774b368b1da8c3ff80be399bcf6fa63b5cff658b6889fc00109da218e23,
governance action
baffa774b368b1da8c3ff80be399bcf6fa63b5cff658b6889fc00109da218e23#0,
vote tx
009801303fc5cc3c3dfe474c30cc4b7d31e99b5af29467cc317072ea6b728c45,
reward account
b2b7201c62e43ae8e03b61c96931379ebbcdce61befc3f4e4b1f4be4, reward
0 -> 2000000 -> 0, withdrawal tx
4a87409b52b8104d51d41df7ee562196cf33621f64c4c40985b4aef5ff21e9bd,
fee 456417, materialized output
4a87409b52b8104d51d41df7ee562196cf33621f64c4c40985b4aef5ff21e9bd#0,
and treasury ADA 200000000 -> 202000000. The legacy withdraw
smoke phase remains a compatibility alias for the same command proof.
The disburse-submit phase copies and patches a short-epoch genesis,
runs registry-init, stake-reward-init, and governance-withdrawal-init
prerequisites, then invokes the shipped devnet disburse-submit
command. The production command consumes the #149 materialized treasury
UTxO, builds through the production disburse/tx-build path,
signs/submits the transaction, verifies the beneficiary output, and
verifies the reduced treasury output. Latest branch evidence is
runs/devnet/20260517T005034Z: submitted tx
0008ab902b2f835624f453af0467d826b02519d7139ec8e84a04c8a9c000011b,
beneficiary output
0008ab902b2f835624f453af0467d826b02519d7139ec8e84a04c8a9c000011b#1
with 1000000 lovelace, treasury input
309e28ed5b95de38258bcc130d6390800b0719f6410b0d5fe6f3c33cc1b70817#0,
and treasury lovelace 2000000 -> 1000000.
The swap readiness phase uses the checked-in public
SundaeSwap-finance/sundae-contracts@be33466b7dbe0f8e6c0e0f46ff23737897f45835
order.spend artifact, publishes it as a local DevNet reference
script, and writes swap-ready/registry.json for the later #84 order
build/funding slice. Latest branch readiness evidence is
runs/devnet/20260515T124545Z, script hash
02eee6c4d128c9700c178922163645f1fdb381bbdce071acbbd49465,
reference UTxO
490b9bc8a80e8a55434b895bea6ca47fc612105c0cf71b781a61e99cd2be46af#0,
and local order address
addr_test1xqpwaeky6y5vjuqvz7yjy93kghclmvuph0wwqudvh02fgegzamnvf5fge9cqc9ufygtrv303lkecrw7uupc6ew75j3jsdhyjpu.
This is readiness evidence only; it is not a built, funded, submitted,
or spent swap order.
The DevNet bootstrap recovery is split into registry/reference-script publication (#147), stake/reward setup (#148), governance funding and treasury withdrawal setup (#149), and disburse/beneficiary receipt (#150). Older evidence slices also track governance action (#82), withdrawal (#83), disburse (#86), SundaeSwap V3 contract readiness (#132), SundaeSwap V3 order build/funding (#84), SundaeSwap V3 order spend (#85), and reorganize (#87). Do not record swap-order build/funding, swap-spend, or reorganize evidence until those slices land and their phase-specific smoke commands pass. SundaeSwap compatibility claims must be based on the public V3 contracts/SDK, not on an Amaru-only toy validator.
Release note wording for this slice:
Local DevNet disburse-submit evidence now proves #150: the smoke runs
registry-init, stake-reward-init, and governance-withdrawal-init
prerequisites, invokes the shipped `devnet disburse-submit` command,
builds/signs/submits the disburse through the production disburse and
tx-build path, verifies beneficiary receipt, and verifies the reduced
treasury output. Evidence: runs/devnet/20260517T005034Z, submitted tx
0008ab902b2f835624f453af0467d826b02519d7139ec8e84a04c8a9c000011b,
beneficiary output
0008ab902b2f835624f453af0467d826b02519d7139ec8e84a04c8a9c000011b#1
with 1000000 lovelace, treasury input
309e28ed5b95de38258bcc130d6390800b0719f6410b0d5fe6f3c33cc1b70817#0,
and treasury lovelace 2000000 -> 1000000. This is not SundaeSwap order
or reorganize proof.
Local DevNet governance-withdrawal-init evidence now proves #149: the
smoke runs registry-init and stake-reward-init prerequisites, invokes
the shipped `devnet governance-withdrawal-init` command, submits and
votes through the treasury-withdrawal governance action, observes the
reward account funded, builds/signs/submits the withdrawal through the
production withdraw/tx-build path, and records the materialized treasury
UTxO handoff for #150. Evidence: runs/devnet/20260516T231003Z,
proposal baffa774b368b1da8c3ff80be399bcf6fa63b5cff658b6889fc00109da218e23,
action baffa774b368b1da8c3ff80be399bcf6fa63b5cff658b6889fc00109da218e23#0,
vote 009801303fc5cc3c3dfe474c30cc4b7d31e99b5af29467cc317072ea6b728c45,
reward account b2b7201c62e43ae8e03b61c96931379ebbcdce61befc3f4e4b1f4be4,
reward 0 -> 2000000 -> 0 lovelace, withdrawal
4a87409b52b8104d51d41df7ee562196cf33621f64c4c40985b4aef5ff21e9bd,
fee 456417 lovelace, materialized output
4a87409b52b8104d51d41df7ee562196cf33621f64c4c40985b4aef5ff21e9bd#0,
and treasury ADA 200000000 -> 202000000. This is not disburse,
SundaeSwap order, or reorganize proof.
Local DevNet swap readiness evidence now proves the prerequisite for
the SundaeSwap order-build slice: the smoke hashes the checked-in public
SundaeSwap V3 order.spend artifact, publishes it as a local DevNet
reference script, and writes a readiness registry for #84. Evidence:
runs/devnet/20260515T124545Z, source
SundaeSwap-finance/sundae-contracts@be33466b7dbe0f8e6c0e0f46ff23737897f45835,
script hash
02eee6c4d128c9700c178922163645f1fdb381bbdce071acbbd49465,
reference UTxO
490b9bc8a80e8a55434b895bea6ca47fc612105c0cf71b781a61e99cd2be46af#0.
This is not swap order build, funding, submission, or spend proof.
The release workflows run scripts/release/check-version-consistency
before building. A tag is publishable only when:
- the tag version matches
amaru-treasury-tx.cabal; CHANGELOG.mdcontains a section for that version;- release-please state files are absent.
Linux publishes AppImage, DEB, and RPM assets. Darwin publishes an
aarch64-darwin tarball and updates the Homebrew tap. Each release
bundle is smoke-tested before upload; Linux extracts the generated
artifacts with .#linux-artifact-smoke and checks the
swap-wizard --help signer surface, while Darwin installs the
tap-qualified formula and runs its Homebrew test.
Linux builds the .#linux-release-artifacts flake package, which
produces:
amaru-treasury-tx-<version>-x86_64-linux.AppImage;amaru-treasury-tx.AppImage, the unversioned AppImage alias;amaru-treasury-tx-<version>-x86_64-linux.deb;amaru-treasury-tx-<version>-x86_64-linux.rpm;SHA256SUMS.
Binary bundling for AppImage, DEB, and RPM lives in
nix/linux-release.nix. The Linux Release workflow builds the flake
package, runs the shared smoke app against the result, and then uploads
the staged files.
Darwin builds the .#darwin-release-artifacts flake package, which
produces:
amaru-treasury-tx-<version>-aarch64-darwin.tar.gz;SHA256SUMS;amaru-treasury-tx.rb, the Homebrew formula with the tarball SHA.
The Darwin Release workflow uploads the tarball and copies the generated
formula into the Homebrew tap. The smoke test installs
lambdasistemi/tap/amaru-treasury-tx, runs brew test, and checks the
offline help surfaces plus executable presence for the shipped tools.
Binary bundling and dylib load-path patching live in
nix/darwin-release.nix.
Linux dev bundles¶
The existing Linux Release workflow also has a manual dev mode:
gh workflow run release.yml \
--ref main \
-f mode=dev-linux
Dev mode builds the .#linux-dev-release-artifacts flake package on
the NixOS runner, uploads the AppImage, DEB, RPM, and checksums to the
dev-linux prerelease, downloads the versioned assets back from that
release, and runs .#linux-artifact-smoke against the downloaded files.
On pull requests that touch the Linux workflow or Nix packaging, the same workflow runs in build-only dev mode and does not mutate releases.
Darwin dev Homebrew formula¶
The existing Darwin Release workflow also has a manual dev mode:
gh workflow run darwin-release.yml \
--ref main \
-f mode=dev-homebrew \
-f update_tap=yes
Dev mode builds the .#darwin-dev-homebrew-artifacts flake package on
macos-14, uploads the tarball to the dev-homebrew prerelease, and
updates Formula/amaru-treasury-tx-dev.rb in
lambdasistemi/homebrew-tap.
The dev formula uses the Cabal version plus source revision and conflicts
with the release formula because both install the same command-line
tools. After updating the tap, the workflow installs
amaru-treasury-tx-dev through Homebrew and smoke-tests all shipped
tools through the tap-qualified formula
lambdasistemi/tap/amaru-treasury-tx-dev, including brew test. The
test avoids node-socket-dependent commands, so swap-probe is checked
for executable presence rather than invoked. On pull requests that touch
the Darwin workflow or Nix packaging, the same workflow runs in
build-only dev mode and does not mutate releases or the Homebrew tap;
it still verifies the generated formula by installing it through a
temporary local lambdasistemi/tap tap whose URL points at the freshly
built tarball.