Release backend module
release-backend-module.yml cuts a Maven release with maven-release-plugin: it commits the release version, tags
it, pushes both, deploys the tagged release, deploys the next development SNAPSHOT, and then creates a GitHub Release
with generated notes. It works for any Maven project, not only OpenMRS modules.
Usage
Releases are usually started by hand:
name: Release
on:
workflow_dispatch:
inputs:
release_version:
description: 'Release version (defaults to the POM version without -SNAPSHOT)'
required: false
development_version:
description: 'Next development version'
required: false
jobs:
release:
permissions:
contents: write
uses: openmrs/openmrs-contrib-gha-workflows/.github/workflows/release-backend-module.yml@main
with:
release_version: ${{ inputs.release_version }}
development_version: ${{ inputs.development_version }}
secrets:
MAVEN_REPO_USERNAME: ${{ secrets.MAVEN_REPO_USERNAME }}
MAVEN_REPO_API_KEY: ${{ secrets.MAVEN_REPO_API_KEY }}
APP_ID: ${{ secrets.OMRS_MODULE_RELEASE_APP_ID }}
APP_PRIVATE_KEY: ${{ secrets.OMRS_MODULE_RELEASE_APP_PRIVATE_KEY }}What it does
- Checks credentials: both Maven secrets must be set, and
APP_ID/APP_PRIVATE_KEYmust be set together or not at all. - Resolves the branch:
branchif set, otherwise the triggering branch, which must bemain,master, the repository’s default branch, or a version branch such as2.xor2.8.x. - Resolves the versions (see Versions) and checks the release tag does not exist yet.
- Runs the pre-release checks.
- Writes
~/.m2/settings.xmlwith credentials for the release and snapshot server IDs, and imports the GPG key if one is given. - Runs
release:prepareandrelease:performwithmaven-release-plugin(see Plugin version), using an empty local repository so nothing cached from earlier builds reaches the release.release:performchecks the tag out from the local clone (localCheckout), so private repositories need no extra credentials. Ifrelease:preparefails,release:rollbackis attempted. - Deploys the next development SNAPSHOT with
mvn deploy -DskipTestsand the same profiles. If only this step fails, the release itself is done and should not be repeated; the GitHub Release is still created. - If
create_github_releaseis true, waits for the tag to be visible, generates release notes against the previous release of the same artifact, and creates the GitHub Release.
Tests are skipped in the release builds. They are expected to have run in the build workflow (Build backend module or Build Maven project) before a release is cut.
Versions
- Release version:
release_version, or the POM version without-SNAPSHOT. If the POM version is not a SNAPSHOT,release_versionis required. Pre-release suffixes such as-alpha,-beta.1or-rc.2are valid releases;-SNAPSHOTis not. On a version branch, the release version must start with the branch prefix (2.8.xreleases2.8.*). - Development version:
development_version(-SNAPSHOTis appended if missing). If omitted, a pre-release such as1.2.0-beta.1continues as1.2.0-SNAPSHOT, anX.Y.0release asX.(Y+1).0-SNAPSHOT, and any other release leaves the choice tomaven-release-plugin. - Tag: the plugin’s
tagNameFormatfrom the effective POM, defaulting to@{project.artifactId}-@{project.version}. Only the@{project.artifactId}and@{project.version}placeholders are supported. - GitHub Release: a release with a pre-release suffix is marked as a prerelease and is not made the latest release.
Release notes compare against the latest stable release of the same artifact older than this one, so a
2.xpatch does not diff against a newer3.xtag.
Plugin version
The release uses the maven-release-plugin version the POM declares, raised to 3.1.1 if it is older. A POM that does
not declare one gets 3.3.1. Maven itself pins a default version (2.5.3 before Maven 3.9, 3.0.1 since), which counts
as not declared, so declaring exactly that version also gets 3.3.1.
Pre-release checks
These run before anything is built, so a misconfigured repository fails in seconds:
- SCM: the POM must have
<scm><developerConnection>(or<connection>), whichmaven-release-pluginneeds. Whatever repository it names, the plugin’s pushes and checkouts are redirected to the repository running the release, so an outdated or upstream URL still works; a notice suggests updating it. - Release target: outside the
openmrsorg, the release fails if the release or snapshot repository is one OpenMRS owns, or if it would publish anorg.openmrsgroupId to Maven Central (by URL or withcentral-publishing-maven-plugin). The rules are the same as for SNAPSHOT deploys.
Repositories and credentials
The release and the next SNAPSHOT deploy to <distributionManagement> from the effective POM, unless
maven_release_repository_url or maven_snapshot_repository_url is set, in which case they are passed to the deploy
plugin as alternate repositories.
Both server IDs get MAVEN_REPO_USERNAME and MAVEN_REPO_API_KEY in the generated settings.xml. The IDs are
maven_release_server_id / maven_snapshot_server_id if set, otherwise those in <distributionManagement>,
otherwise openmrs-repo-modules / openmrs-repo-snapshots. A repository that uses one ID for both is fine.
Signing and profiles
- GPG: pass
GPG_PRIVATE_KEY(ASCII-armored) andGPG_PASSPHRASEto sign artifacts. Without a key, nothing is signed. - Profiles:
maven_profilesactivates comma-separated profiles formaven-release-plugin, its release builds and the SNAPSHOT deploy, for example the profile that configuresmaven-gpg-pluginandcentral-publishing-maven-pluginfor Maven Central. The profiles are also active when the workflow reads the POM, so settings in a profile, such as<distributionManagement>or the plugin’stagNameFormat, are used for the checks, server IDs and tag name. A profile that is not activated does not count as publishing to Maven Central. - Maven Central SNAPSHOTs: with
central-publishing-maven-pluginin the profile, the SNAPSHOT deploy after the release also goes to Central, and fails unless SNAPSHOT publishing is enabled for your namespace. The release itself is complete by then and the GitHub Release is still created.
Pushing the release
The release commit and tag are pushed with, in order of preference, a GitHub App token (APP_ID +
APP_PRIVATE_KEY), BOT_PAT, or github.token. A GitHub App token does not bypass branch protection on its own: add
the App to the bypass list of any ruleset that protects the release branch. github.token cannot bypass protection,
so with it the push fails later if the branch is protected.
App installation tokens expire after one hour. The push happens during release:prepare, before the longer
release:perform, so this is rarely a problem. For an unusually slow build before the push, use BOT_PAT.
OpenMRS defaults
OpenMRS modules use the OMRS_MODULE_RELEASE GitHub App (installed on the module and on its ruleset bypass list) and
the MAVEN_REPO_USERNAME / MAVEN_REPO_API_KEY org secrets. Deploy repositories come from the OpenMRS module parent
POM, and release commits are authored as openmrs-bot <infrastructure@openmrs.org>.
Outside OpenMRS
- Set
publish, which defaults tofalseoutside theopenmrsorg, so the release fails without it. Set it from the repository owner, e.g.publish: ${{ github.repository_owner == 'my-org' }}, so that forks cannot release. - Set
git_user_nameandgit_user_email; the defaults identify the OpenMRS bot. - Check
<distributionManagement>. Projects created from OpenMRS templates or forks often still point at OpenMRS repositories, which the pre-release checks reject. maven-release-pluginrefuses to release with SNAPSHOT dependencies. Update them to released versions first.- Map differently named secrets in the caller, e.g.
MAVEN_REPO_USERNAME: ${{ secrets.MY_MAVEN_USER }}.secrets: inheritdoes not rename secrets. - If the POM has no
<distributionManagement>(for example, the URL is normally passed on the command line), set the repository URL inputs and, if needed, the server ID inputs.
Permissions
Both jobs request contents: write, so the calling job must grant it.
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
release_version | string | Release version (e.g., 1.3.3 or 1.4.0-beta.1). If omitted, the POM version without -SNAPSHOT. | |
development_version | string | Next development version (e.g., 1.4.0-SNAPSHOT). If omitted, a pre-release such as X.Y.Z-beta.1 continues as X.Y.Z-SNAPSHOT, an X.Y.0 release as X.(Y+1).0-SNAPSHOT, and maven-release-plugin picks any other. | |
publish | boolean | ${{ github.repository_owner == 'openmrs' }} | Whether this repository may release. Defaults to true in the openmrs org and false elsewhere, so forks that inherit a caller workflow do not release; outside the openmrs org, set it from an expression such as github.repository_owner == 'my-org'. When false, the release fails. |
java_version | string | Java version to use for the release. If omitted, inferred from Maven compiler settings in pom.xml. | |
java_distribution | string | temurin | Java distribution |
git_user_name | string | openmrs-bot | Git committer name for release commits |
git_user_email | string | infrastructure@openmrs.org | Git committer email for release commits |
branch | string | Branch to release from. If omitted, uses the branch that triggered the workflow. | |
maven_release_server_id | string | Maven server ID for release deployment. If omitted, inferred from <distributionManagement><repository><id> in pom.xml. | |
maven_snapshot_server_id | string | Maven server ID for snapshot deployment. If omitted, inferred from <distributionManagement><snapshotRepository><id> in pom.xml. | |
maven_release_repository_url | string | Repository URL to deploy the release to instead of <distributionManagement><repository>. | |
maven_snapshot_repository_url | string | Repository URL to deploy the next SNAPSHOT to instead of <distributionManagement><snapshotRepository>. | |
maven_profiles | string | Comma-separated Maven profiles to activate in the release builds (e.g. release,sign). | |
create_github_release | boolean | true | Whether to create a GitHub Release with release notes after Maven release |
Secrets
| Name | Description |
|---|---|
MAVEN_REPO_USERNAME required | Maven repository username |
MAVEN_REPO_API_KEY required | Maven repository API key / password |
GPG_PRIVATE_KEY | ASCII-armored GPG private key for signing release artifacts. When omitted, artifacts are not signed. |
GPG_PASSPHRASE | Passphrase for the GPG private key |
APP_ID | GitHub App ID for the module release bot. When set with APP_PRIVATE_KEY, a short-lived App token is preferred over BOT_PAT. The App must be on the target ruleset bypass list to push past branch protection. |
APP_PRIVATE_KEY | Private key for the module release bot GitHub App. |
BOT_PAT | Legacy Personal Access Token for bypassing branch protections. Fallback when App credentials are not provided. |