Releases are driven by a single command and a single source of truth
(gradle.properties version=). You never hand-edit version strings — the
version is injected at build time into Signum.VERSION, the OpenAPI spec, and the
Windows installer.
From an up-to-date, clean develop:
git switch develop
git pull
./gradlew releaseTagAnswer the three prompts (bump / hard fork? / notes). The task opens a release PR
into develop. Merge it, promote develop → main, and the tag + GitHub release
are produced automatically.
- You are on
develop, the working tree is clean, and it is in sync with origin. (The task refuses to run otherwise.) - The
ghCLI is installed and authenticated, so the release PR can be opened for you. (If it is missing, the task still pushes the branch and prints a link to open the PR manually.) - One-time, by a repo admin: the
RELEASE_TAG_PATsecret exists (see One-time setup).
./gradlew releaseTagYou will be asked for:
- bump —
patch|minor|major. Computed from the currentgradle.propertiesversion (e.g.3.9.11+minor→3.10.0). - hard fork? — a mandatory-upgrade release. When
yes, the bump must be at leastminor(apatchis rejected) and the changelog/release are marked with a mandatory-upgrade warning. - release notes — a single line. You can refine the full notes later by
editing
CHANGELOG.mdon the release branch before the PR is merged.
What the task does, in order:
- Pre-flight guards (on
develop, clean tree, in sync with origin). - Full build and tests — it stops here if anything fails, before prompting.
- Creates
release/<version>, bumpsgradle.properties, prepends aCHANGELOG.mdsection, commits (release: vX.Y.Z), pushes the branch, and opens a PR intodevelop.
Review the release PR (refine the CHANGELOG entry on the branch if needed) and
merge it into develop.
Merge develop into main as usual. This is the only manual git step after the
PR; it is intentionally a human action.
After the push to main, the Build Signum Node workflow runs the build and
tests. Only when it succeeds does the auto-tag workflow run (it is triggered
by that workflow's successful completion, not by the raw push). It reads the
version from gradle.properties at the built commit and, if no vX.Y.Z tag
exists yet, creates and pushes it. This means a tag can only exist for a commit
whose build and tests were green. That tag triggers:
- release.yml — builds the Windows artifacts and drafts a GitHub release whose
body is the matching
CHANGELOG.mdsection (titled with a hard-fork warning when applicable). - dockerhub.yml — builds and pushes the multi-arch Docker images.
Review the drafted GitHub release and publish it.
Provide the answers as properties to skip the prompts:
./gradlew releaseTag \
-Prelease.bump=minor \
-Prelease.hardFork=true \
-Prelease.notes="What changed in this release"Additional properties:
-Prelease.notesFile=<path>— read multi-line notes from a file.-Prelease.branch=<branch>— release from a branch other thandevelop.-Prelease.repo=<owner/name>— repo slug used for the manual-PR link fallback.
The auto-tag job pushes the tag using the RELEASE_TAG_PAT repository secret — a
PAT (fine-grained or classic) with contents: write on this repo. This is
required: a tag pushed with the default GITHUB_TOKEN would not trigger the
downstream release.yml / dockerhub.yml workflows. Add it under
Settings → Secrets and variables → Actions.
gradle.properties (version=X.Y.Z) ← single source of truth, bumped by releaseTag
│ build.gradle computes nodeVersion
├── version.properties resource → BuildInfo → Signum.VERSION (runtime)
├── OpenAPI info.version (bundled spec)
└── Windows installer app-version
CI builds pass -PreleaseBuild so released artifacts always carry a clean
(non-prerelease) version. Local and CI builds that are not on a release tag are
suffixed -dev and flagged as prereleases (a -dev node refuses to run on
mainnet).