Full examples
These are complete caller workflows for a repository outside the openmrs organization. Replace the acme names,
URLs and secret names with your own. Each workflow page lists every input; these examples only set what a typical
implementer needs.
Frontend module on npmjs
A frontend module usually has a single ci.yml that builds every push and pull request, then releases from the same
run, so nothing is published unless the build passed:
- every push to
mainpublishes a pre-release to thenextdist-tag; - running the workflow by hand cuts a tagged release, bumping the version, tagging it, publishing to
latestand creating a GitHub Release.
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
inputs:
release_type:
description: 'Release version bump'
type: choice
options: [patch, minor, major]
default: patch
release_version:
description: 'Exact version to release, e.g. 2.1.0. Overrides the bump above.'
required: false
publish_only:
description: 'Only re-publish an existing release tag after a failed publish. Set the exact version too.'
type: boolean
default: false
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
build:
uses: openmrs/openmrs-contrib-gha-workflows/.github/workflows/build-frontend-module.yml@main
release:
needs: build
permissions:
contents: write
id-token: write
uses: openmrs/openmrs-contrib-gha-workflows/.github/workflows/release-frontend-module.yml@main
with:
publish: ${{ github.repository_owner == 'acme' }}
release_type: ${{ inputs.release_type }}
release_version: ${{ inputs.release_version }}
publish_only: ${{ inputs.publish_only || false }}
publish_environment: npm-publish
git_user_name: acme-release-bot
git_user_email: release-bot@acme.example
secrets:
APP_ID: ${{ secrets.RELEASE_APP_ID }}
APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}What this relies on:
- Opting in. Outside the
openmrsorganization nothing is published unlesspublishis true. Tying it to the owner means forks of this repository, which run the sameci.yml, build without releasing. - Trusted publishing. Each package has a trusted publisher on npmjs naming this repository,
ci.ymland thenpm-publishenvironment, which must exist under the repository’s Settings → Environments. No npm token is passed; see Credentials. A package that is not on npmjs yet needs one first publish by hand or with a token. - Pull requests. The
releasejob is called for pull requests too, but publishes nothing for them. - Releases. The workflow pushes a release commit and tag to
main.RELEASE_APP_IDandRELEASE_APP_PRIVATE_KEYbelong to a GitHub App onmain’s ruleset bypass list; with no protection onmain, drop thesecretsblock and the job’s own token is used. - Concurrency. A new commit cancels an outdated pull request build. Runs on
mainare never cancelled once started, but GitHub keeps only one waiting run per group, so a push that is still queued is replaced by a newer push. The event name in the group keeps releases started by hand in a group of their own, so a burst of pushes cannot drop a queued release.
A fork of an OpenMRS frontend module cannot publish @openmrs/* packages to npmjs. Rename the packages to your own
scope, publish to another registry (below), or keep only the build job.
Frontend module on a private registry
To publish to a registry other than npmjs, such as a company feed, point the release at it and pass a token. This works
for @openmrs/* names too. Only npmjs is tested; other registries are supported on a best-effort basis.
jobs:
build:
uses: openmrs/openmrs-contrib-gha-workflows/.github/workflows/build-frontend-module.yml@main
release:
needs: build
permissions:
contents: write
uses: openmrs/openmrs-contrib-gha-workflows/.github/workflows/release-frontend-module.yml@main
with:
publish: ${{ github.repository_owner == 'acme' }}
npm_registry_url: https://npm.acme.example/
release_type: ${{ inputs.release_type }}
release_version: ${{ inputs.release_version }}
publish_only: ${{ inputs.publish_only || false }}
git_user_name: acme-release-bot
git_user_email: release-bot@acme.example
secrets:
NPM_AUTH_TOKEN: ${{ secrets.ACME_NPM_TOKEN }}
APP_ID: ${{ secrets.RELEASE_APP_ID }}
APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}The triggers and concurrency are the same as above. Instead of npm_registry_url, you can set npmPublishRegistry
in .yarnrc.yml or publishConfig.registry in each package.json; see
Registry. If the build also installs packages from that registry,
configure it in .yarnrc.yml as you would locally.
Backend module
Backend modules usually keep two workflows: one that builds every push and deploys SNAPSHOTs, and a release started by hand once the build on the branch is green. The release does not run the tests again.
name: Build with Maven
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
build:
uses: openmrs/openmrs-contrib-gha-workflows/.github/workflows/build-backend-module.yml@main
with:
deploy_snapshots: ${{ github.repository_owner == 'acme' }}
maven_snapshot_repository_url: https://maven.acme.example/repository/snapshots/
secrets:
MAVEN_REPO_USERNAME: ${{ secrets.ACME_MAVEN_USER }}
MAVEN_REPO_API_KEY: ${{ secrets.ACME_MAVEN_TOKEN }}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 (optional)'
required: false
jobs:
release:
permissions:
contents: write
uses: openmrs/openmrs-contrib-gha-workflows/.github/workflows/release-backend-module.yml@main
with:
publish: ${{ github.repository_owner == 'acme' }}
release_version: ${{ inputs.release_version }}
development_version: ${{ inputs.development_version }}
maven_release_repository_url: https://maven.acme.example/repository/releases/
maven_snapshot_repository_url: https://maven.acme.example/repository/snapshots/
git_user_name: acme-release-bot
git_user_email: release-bot@acme.example
secrets:
MAVEN_REPO_USERNAME: ${{ secrets.ACME_MAVEN_USER }}
MAVEN_REPO_API_KEY: ${{ secrets.ACME_MAVEN_TOKEN }}
APP_ID: ${{ secrets.RELEASE_APP_ID }}
APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}What this relies on:
- Opting in.
deploy_snapshotsandpublishdefault to false outside theopenmrsorganization. Tied to the owner, they stay off in forks of this repository. - Deploy targets. The repository URL inputs override
<distributionManagement>, which in a fork of an OpenMRS module usually still names OpenMRS repositories. If your POM already points at your own repositories, leave the URL inputs out. - Build only. Without a Maven repository, drop
deploy_snapshots, the URL input and thesecretsfrombuild.yml. - SCM. The POM needs an
<scm>section, but its URL may still name the upstream repository; the release pushes to the repository running it. - Maven Central. To sign and publish with
central-publishing-maven-plugin, setmaven_profilesto the profile that configures it, setmaven_release_server_idto the plugin’spublishingServerId(centralby default) so your Central token is written under that ID, and passGPG_PRIVATE_KEYandGPG_PASSPHRASE. A fork cannot publishorg.openmrsgroupIds there. The release’s final step deploys the next SNAPSHOT with the same profile, so it goes through the plugin too and fails unless SNAPSHOT publishing is enabled for your namespace on Central. That failure comes after the release is published, and the GitHub Release is still created.