Apache Fineract is a top-level project, so this follows the standard TLP release process: the vote
happens on dev@fineract.apache.org and needs three binding +1 votes from PMC members. There
is no Incubator step and nothing in the artifact name says "incubating".
Read this alongside the ASF's own documents, which are authoritative where this disagrees with them:
The release is the source. A convenience container may be published alongside it, but the thing being voted on is a signed source tarball from which the application can be built.
Do these once, well before a release, because each has a delay you cannot compress on the day.
- Generate a code-signing key (4096-bit RSA), if you do not have one.
gpg --full-generate-key gpg --list-secret-keys --keyid-format=long
- Publish it to a public keyserver and to your
people.apache.orgprofile. - Add it to the project KEYS file, which lives in the distribution area rather than in git:
svn co https://dist.apache.org/repos/dist/release/fineract fineract-dist cd fineract-dist (gpg --list-sigs "<your email>"; gpg --armor --export "<your email>") >> KEYS svn commit -m "Add <your name> release signing key"
- Get the key into the ASF web of trust if you can — signatures from other members make the key verifiable rather than merely present.
Four things need a PMC answer before a candidate is worth cutting. They are tracked on the release issue rather than decided here.
| Question | Why it has to be settled first |
|---|---|
| Source only, or source plus a container? | Determines attribution obligations and whether the container work is release-blocking |
| Which Fineract version does this target? | Everything is currently validated against Fineract head; a release aimed at a released Fineract needs that compatibility evidenced first |
| The version number | See §2 |
Does the "not release-ready" notice in security.md come out? |
It stays until the PMC says otherwise |
package.json carries the version, and it is not cosmetic: it becomes the
metadata.component.version and the package URL of any SBOM generated from the tree, which is one
of the artifacts a PMC reviews.
- Release candidates:
1.0.0-rc.1,1.0.0-rc.2, … - Releases:
1.0.0
npm run check:version rejects 0.0.0 and anything that is not SemVer, and on a tag build it
asserts the tag names the same version package.json does. Set the version in the release
commit, then tag that commit — not the other way round.
# 1. On a clean checkout of the commit being released.
git status --porcelain # must be empty
npm ci
# 2. Set the version, update CHANGELOG.md, commit and sign.
# (edit package.json + CHANGELOG.md)
git commit -S -m "release: 1.0.0-rc.1"
# 3. Every gate, green, before anything is tagged.
npm run lint:prune
npm run test:eslint-rules
npm run format:check
npm run i18n:check
npm run check:icons
npm run check:internal-endpoints
npm run check:route-permissions
npm run check:version
npm run typecheck:e2e
npm run api:surface
npm run check:licenses
npm run check:licenses:selftest
bash scripts/check-license.sh
npm run build
npm run test -- --watch=false --browsers=ChromeHeadless
npm run ga:check
# 4. The end-to-end suites, against a real Fineract.
bash scripts/e2e-stack.sh --fresh
npm run test:e2e:local
bash scripts/e2e-stack-2fa.sh
npm run test:e2e:2fa
# 5. Tag it, signed.
git tag -s 1.0.0-rc.1 -m "Apache Fineract Backoffice UI 1.0.0-rc.1"
git push origin 1.0.0-rc.1ng test runs two projects, and a build failure in one prints the other project's success line.
Grep the output for ✘ and FAILED; never trust the final line alone.
VERSION=1.0.0-rc.1
NAME=apache-fineract-backoffice-ui-$VERSION-src
# The tarball is built from the tag, through git, so nothing untracked can leak into it —
# no node_modules, no .env, no local audit output.
git archive --format=tar.gz --prefix="$NAME/" -o "$NAME.tar.gz" "$VERSION"
sha512sum "$NAME.tar.gz" > "$NAME.tar.gz.sha512"
gpg --armor --detach-sign "$NAME.tar.gz"
# Verify what you are about to ask people to vote on.
gpg --verify "$NAME.tar.gz.asc" "$NAME.tar.gz"
sha512sum -c "$NAME.tar.gz.sha512"This is the step most easily got wrong, and getting it wrong makes the evidence worthless. A RAT
run over a git checkout says nothing about the artifact: the checkout has a .rat-excludes
covering build output that is not in the tarball, and the tarball may contain files the checkout's
.gitignore hid from you.
tar xzf "$NAME.tar.gz"
cd "$NAME"
curl -sL https://repo1.maven.org/maven2/org/apache/rat/apache-rat/0.17/apache-rat-0.17.jar \
-o apache-rat.jar
echo "401939ebe5a52c6ed524029897bf914eaaba503d36c069ebcdbd8847a9e7cf93 apache-rat.jar" \
| sha256sum --check
java -jar apache-rat.jar -E .rat-excludes -d . # exit 1 if anything is unapprovedcd "$NAME"
npm ci
npm run buildA source release nobody can build from is not a source release.
npm run check:licenses -- --json > "$NAME-licenses.json"
npx --yes @cyclonedx/cyclonedx-npm --ignore-npm-errors --omit dev \
--output-format JSON --spec-version 1.6 --output-file "$NAME-sbom.cdx.json"Publish both alongside the candidate so reviewers can check the claims instead of taking them.
svn co https://dist.apache.org/repos/dist/dev/fineract fineract-dev
mkdir -p fineract-dev/backoffice-ui/$VERSION
cp $NAME.tar.gz* fineract-dev/backoffice-ui/$VERSION/
cd fineract-dev && svn add --force . && svn commit -m "Stage Backoffice UI $VERSION"On dev@fineract.apache.org, open for at least 72 hours, and it does not close until there
are three binding +1 votes from PMC members.
Subject: [VOTE] Release Apache Fineract Backoffice UI 1.0.0-rc.1
Hello,
This is a vote to release Apache Fineract Backoffice UI 1.0.0-rc.1.
Git tag: 1.0.0-rc.1
Commit: <full sha>
Artifacts: https://dist.apache.org/repos/dist/dev/fineract/backoffice-ui/1.0.0-rc.1/
KEYS: https://dist.apache.org/repos/dist/release/fineract/KEYS
Release notes: <link to CHANGELOG.md at the tag>
Checks run against this commit:
- Unit tests: <n> passing
- E2E against apache/fineract:<version>: <n> passing
- Apache RAT over the tarball: <n> approved, 0 unapproved
- Dependency licences: <n> Category A, <n> Category B, 0 Category X, 0 unclassified
- GA readiness gate: <n>/<n>, 0 blocking
- SBOM (CycloneDX 1.6): attached
Known limitations are in CHANGELOG.md and are not defects introduced by this release.
Please review and vote:
[ ] +1 release this package
[ ] +0 no opinion
[ ] -1 do not release, because ...
This vote is open for at least 72 hours.
Say so in the thread, because a vote where nobody checked is not a vote:
gpg --import KEYS
gpg --verify apache-fineract-backoffice-ui-1.0.0-rc.1-src.tar.gz.asc
sha512sum -c apache-fineract-backoffice-ui-1.0.0-rc.1-src.tar.gz.sha512
tar xzf apache-fineract-backoffice-ui-1.0.0-rc.1-src.tar.gz
# LICENSE and NOTICE present and correct; no unexpected binaries; RAT clean; it builds.Only after the vote passes.
svn mv https://dist.apache.org/repos/dist/dev/fineract/backoffice-ui/$VERSION \
https://dist.apache.org/repos/dist/release/fineract/backoffice-ui/$VERSION \
-m "Release Backoffice UI $VERSION"Wait for downloads.apache.org to pick it up (minutes) and for the CDN to settle (allow an hour)
before announcing. Then post the result to dev@, announce on announce@apache.org, and update
the download page.
Old releases come out of dist/release/ as they are superseded — that area holds current
releases, and the archive keeps the history.
- Bump
package.jsonto the next development version. - Open the next
CHANGELOG.mdsection. - Move anything deferred out of the release notes and onto issues, so the next release manager inherits a list rather than a memory.
- A gate that existed and was never run.
scripts/ga-check.mjsfailed for weeks before CI was wired to run it. Run the gates listed in §3 yourself; do not infer them from a green badge. - A gate that stopped checking without failing.
ga-checktreats an undetermined gate as a blocking failure for exactly this reason — a check that cannot read its input must not report a pass. - RAT over the working tree. See §4.
- The container that served its own HTML for every API call. It built, served its landing page,
and could not reach a backend at all. The
Container ImageCI job now asserts a proxied API response, because a 200 on/proves nothing.