Release the project

The release is driven by an interactive script at the root of the repository:

$ ./release.sh

Run ./release.sh --help for the full list of options.

Script options

--local

Full local rehearsal: release branch, Maven build with the release profile (javadoc, sources, GPG signing), tag, and release notes generation. Nothing is published remotely (no Maven Central, Docker Hub, git push, GitHub release, or production email).

--skip-tests

Adds -DskipTests to Maven build commands (also prefilled in the Extra Maven options prompt).

--dry-run

Simulates the workflow without running git or Maven commands.

--rollback

Undoes a local or failed release using the release/.release state file (under the gitignored release/ directory). Deletes the local release branch and tag, checks out the original branch, and removes release/.release.

Typical local rehearsal:

$ cp .env.example .env
$ ./release.sh --local --skip-tests

If the release fails, or you want to discard the local rehearsal:

$ ./release.sh --rollback

The release/.release file is written as soon as the release branch is created, so --rollback works even when the build fails midway.

What the script does (production release)

  • Create a release branch

  • Replace the SNAPSHOT version by the final version number

  • Commit the change

  • Build the final artifacts using the release profile (javadoc, sources, GPG signing)

  • Tag the version

  • Prepare release notes from docs/source/release/{version}.md and GitHub API

  • Deploy to Maven Central using the central-publishing-maven-plugin

  • Prepare the next SNAPSHOT version

  • Commit the change

  • Merge the release branch into the branch you started from

  • Push the changes and the tag to origin

  • Create a GitHub release with gh release create

  • Optionally announce the version on https://discuss.elastic.co/c/annoucements/community-ecosystem

You will be guided through all the steps.

Release notes

Release notes live in Markdown under docs/source/release/ (for example 3.0.md). They are included in the ReadTheDocs documentation via MyST Parser and reused by the release script to build release/{version}/release-notes.md (gitignored work directory at the repo root, outside target/ so mvn clean does not remove them).

The final notes combine:

  • A usage header (scripts/templates/release-header.md)

  • The version-specific Markdown file (MyST {ref} resolved to ReadTheDocs links; heading levels demoted by one so they nest under the header)

  • GitHub-generated changelist (## What's Changed) from gh api .../releases/generate-notes

The early preview during release.sh may show an incomplete GitHub section, because generate-notes runs before the tag is pushed and then resolves against the remote default branch. After a successful git push of the release tag, release.sh regenerates release/{version}/release-notes.md so the GitHub release and announcement email use the correct commit range.

release.sh always runs the full workflow. Working files for a given version are written under release/{version}/ (for example release/3.0/release-notes.md and release/3.0/release.log) so you can open and edit them in the IDE before sending the announcement.

To regenerate notes or resend the announcement without starting another release, call the helper scripts directly.

Regenerate the assembled notes (requires gh authenticated and GITHUB_REPO in .env):

$ python3 scripts/prepare-release-notes.py \
    --version 3.0 \
    --since-tag fscrawler-2.9

Send (or resend) the announcement email from an existing notes file:

$ python3 scripts/send-announcement.py \
    release/3.0/release-notes.md \
    --subject "FSCrawler 3.0 released"

To update notes after a GitHub release was already published, edit the Markdown file, regenerate with prepare-release-notes.py, then run:

$ gh release edit fscrawler-{version} --notes-file release/{version}/release-notes.md

Before releasing

Verify that the project builds correctly with the release profile:

$ mvn clean install -Prelease -DskipTests

Prerequisites:

  • Copy .env.example to .env and configure SMTP credentials and GITHUB_REPO

  • python3 and the GitHub CLI (gh auth login)

  • A clean-ish git working tree on your integration branch

  • GPG signing configured for the Maven release profile

  • ~/.m2/settings.xml with a central server entry (Sonatype Central token) for production deploy

  • Docker Hub credentials when pushing images (or pass -Ddocker.skip)

Environment variables (.env)

See .env.example at the repository root:

Variable

Purpose

GITHUB_REPO

GitHub repository (dadoonet/fscrawler)

SMTP_HOST

SMTP server hostname

SMTP_PORT

SMTP port (465 for SSL)

SMTP_USER

SMTP username

SMTP_PASS

Mailbox password (quote special chars: SMTP_PASS='...')

SMTP_SECURITY

Optional: ssl (port 465) or starttls (port 587)

ANNOUNCE_FROM

Sender address (must match SMTP_USER for Ionos)

ANNOUNCE_TO

Recipient (personal address for local, Elastic list for production)

After deployment, check the publishing status on Central Portal. The central-publishing-maven-plugin is configured with autoPublish enabled, so artifacts are published automatically once validation succeeds.

Maven Central release coordinates are immutable: answering “no” during the post-deploy checks only skips the git merge/push; it does not remove or replace what was published. If a release build on Central is wrong, publish a new version (for example a patch). Docker Hub tags can usually be overwritten by deploying the same tag again.

Release Drafter

The repository uses Release Drafter to maintain a draft GitHub release on each push to main. Tags follow the fscrawler-{version} convention. The final published release uses the hybrid notes assembled by release.sh.

Logs are written to release/<release-version>/release.log. On failure, the script prints the last lines of the log and suggests ./release.sh --rollback.

Note

Only developers with write rights to the Sonatype Central namespace under fr.pilato can perform the release.

Only developers with write rights to the DockerHub repository can push the Docker images.