Releasing
Releases are cut by release-please and published by GitHub Actions. Nobody edits a version, creates a tag or pushes an artifact by hand.
What gets published
Everything shares one version, projectVersion in gradle.properties.
| Artifact | Destination | Workflow |
|---|---|---|
io.miragon:bpmn-to-code-runtime, bpmn-to-code-maven, bpmn-to-code-testing | Maven Central | publish-to-maven.yml |
Gradle plugin io.miragon.bpmn-to-code-gradle | Gradle Plugin Portal | publish-to-gradle.yml |
miragon/bpmn-to-code-web:<version> and :latest | Docker Hub | publish-to-docker.yml |
bpmn-to-code-<version>-<platform>.zip, the command-line tool with a Java runtime, for linux-x64, linux-arm64, darwin-arm64, darwin-x64 and win32-x64, plus SHA256SUMS | Assets of the GitHub release, with a build provenance attestation | publish-cli.yml |
Homebrew formula bpmn-to-code | Miragon/homebrew-tap, pull request merged by the Miragon release-please app | publish-homebrew.yml |
.NET tool Miragon.BpmnToCode; Miragon.BpmnToCode.Runtime; Miragon.BpmnToCode.MSBuild | nuget.org | publish-to-nuget.yml, held back, see below |
@miragon/bpmn-to-code; @miragon/bpmn-to-code-runtime | npm | publish-to-npm.yml, held back, see below |
| Deployment of the hosted web app | Pull request in Miragon/ops | update-ops-deployment.yml |
| This documentation site | GitHub Pages | deploy-docs.yml |
Release flow
Everything runs in one workflow, release-please.yml, on every push to main:
- release-please opens or updates the Release PR. It derives the next version from the Conventional Commit titles on
main, updatesCHANGELOG.md, and rewrites the version ingradle.propertiesand in every file listed underextra-filesinrelease-please-config.json. - Merging the Release PR creates the tag (
v<version>) and the GitHub release. - The run then waits at the
releaseenvironment until a maintainer approves it. One approval unblocks all publish jobs. - Maven Central is published first, then the Gradle Plugin Portal. The order matters: the Gradle plugin adds
bpmn-to-code-runtimein its own version to consuming projects, so the runtime has to be resolvable before the plugin is. - In parallel, the Docker image is built for
linux/amd64and pushed as<version>andlatest, andbpmn-to-code-web/README.mdis uploaded as the Docker Hub description. Afterwards a pull request inMiragon/opsbumps the deployment to the new version; it is merged by hand. - In parallel, one runner per platform builds the CLI archive, starts it once without a JDK and the archives are attached to the GitHub release. For a stable release the Homebrew formula is then written from
SHA256SUMS, installed, tested and audited on macOS (arm64, Intel) and Linux, and merged intoMiragon/homebrew-tap;gh workflow run publish-homebrew.yml -f version=<version> -f dry-run=truechecks an existing release without publishing.publish-to-nuget.ymltakes the jars of the Linux archive from the same run, packs them into the .NET tool and packs the runtime and MSBuild packages, runs the tool from the local folder with Java 21, builds a new project on the MSBuild package from that folder, and pushes the packages to nuget.org. It logs in through trusted publishing: the repository variableNUGET_USERnames the nuget.org account, and no API key is stored. The workflow can only be called fromrelease-please.yml, because it needs the archives of the same run.publish-to-npm.ymldoes the same for npm: it builds the runtime package, puts the jars of the Linux archive into the main package, installs both with Java 21 on Linux, macOS and Windows, generates TypeScript and type-checks it, and publishes the runtime before the main package. It also uses trusted publishing, which needs npm 11.5.1 or later, with provenance and without anNPM_TOKEN. Trusted publishing can only be configured for a package that exists, so both packages are published once by hand and then linked to this repository's release workflow in its npm settings. A version that is already on npm is skipped, so a failed run can be repeated, and a prerelease is published under thenexttag. - The documentation is built and deployed.
The Maven Central, Gradle Plugin Portal and Docker workflows run ./gradlew build before they publish; the CLI workflow runs the build of the CLI module on every platform. The NuGet and npm workflows build no Gradle module; they repackage the CLI archives of the same run.
A file that carries the current version as a literal must be listed in extra-files and wrap the version in x-release-please-start-version / x-release-please-end markers. Otherwise it keeps the old number after the next release.
Holding back NuGet and npm
The NuGet and npm packages are not published yet. Until the repository variables PUBLISH_NUGET and PUBLISH_NPM are set to true (Settings → Secrets and variables → Actions → Variables), a release runs publish-to-nuget.yml and publish-to-npm.yml in dry-run mode: the packages are packed and smoke-tested, but not pushed. Setting a variable takes effect with the next release; no code change is needed. Remove the "not yet published" notes in the same step (grep -rnE "not (yet )?published|not on (nuget.org|npm) yet").
Dry run
gh workflow run release-please.yml -f dry_run=trueThis skips the Release PR and the GitHub release and runs every publish job in build-and-verify mode: Maven artifacts go to the local repository, the Gradle plugin is validated only, the image is built but not pushed, the NuGet and npm packages are packed and smoke-tested but not published (npm runs npm publish --dry-run), the ops change is shown as a diff, the docs are built but not deployed. The approval gate still applies. The publish workflows accept the same dry_run input when dispatched on their own.
Documentation
The site is deployed only as part of a release, so a docs change on main is not online until the next release. To publish it earlier:
gh workflow run deploy-docs.ymlSnapshots
A snapshot publishes the state of any branch for trying out unreleased changes. It is dispatched by hand and is not gated by the release environment:
gh workflow run publish-snapshot.yml --ref <branch> -f version=6.3.0-SNAPSHOTThe version must end in -SNAPSHOT; use the upcoming version rather than the one in gradle.properties, since Maven orders the snapshot of an already released version below that release. Snapshots are mutable: publishing the same version again replaces it. The workflow
- builds and tests the branch,
- uploads
bpmn-to-code-runtime,bpmn-to-code-mavenandbpmn-to-code-testingto the Central Snapshots repository, - uploads the Gradle plugin and its plugin marker to the same repository, because the Plugin Portal does not host snapshots (release versions never take this path),
- builds the Docker image and pushes it as
miragon/bpmn-to-code-web:6.3.0-SNAPSHOTonly, without touchinglatestor the Docker Hub description, - reads the digest of the pushed image and opens a pull request in
Miragon/opsthat pins the separate snapshot deployment to that digest. This pull request is merged automatically, so the snapshot instance of the web app rolls out without further action.
Using a snapshot
Gradle resolves the plugin through its marker artifact:
// settings.gradle.kts
pluginManagement {
repositories {
maven { url = uri("https://central.sonatype.com/repository/maven-snapshots/") }
gradlePluginPortal()
}
}
// build.gradle.kts
plugins {
id("io.miragon.bpmn-to-code-gradle") version "6.3.0-SNAPSHOT"
}
repositories {
maven { url = uri("https://central.sonatype.com/repository/maven-snapshots/") }
mavenCentral()
}The repositories entry is needed for the runtime dependency the plugin adds. Maven needs the repository for the plugin and for the runtime dependency:
<pluginRepositories>
<pluginRepository>
<id>central-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases><enabled>false</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</pluginRepository>
</pluginRepositories>
<repositories>
<repository>
<id>central-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases><enabled>false</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>Then use 6.3.0-SNAPSHOT as the version of the plugin and of bpmn-to-code-runtime, configured as in the Gradle and Maven guides.
Building the image locally
./gradlew :bpmn-to-code-web:dockerBuild # fat jar, then the image, tagged <version> and latest
./gradlew :bpmn-to-code-web:dockerRun # starts it detached on http://localhost:9099The tasks call the docker executable found on the PATH. Pushing is left to the workflows.