diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 038d588..75273db 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,5 +1,19 @@
on:
workflow_call:
+ inputs:
+ ref:
+ description: "Branch, tag or SHA to build. Empty = the caller's ref."
+ required: false
+ type: string
+ default: ""
+ artifact-name:
+ description: >
+ Name to upload the build output under. The release flow runs this
+ workflow twice in one run -- once on the branch, once on the tag -- and
+ upload-artifact rejects a duplicate name.
+ required: false
+ type: string
+ default: "dist"
workflow_dispatch:
push:
branches:
@@ -24,7 +38,14 @@ jobs:
matrix:
reqstool-source: [pypi, main]
steps:
+ # Full history and tags: Nisse derives the version from git state, so a
+ # shallow clone would build the wrong number rather than fail.
- uses: actions/checkout@v7
+ with:
+ persist-credentials: false
+ fetch-depth: 0
+ fetch-tags: true
+ ref: ${{ inputs.ref || github.ref }}
- name: Set up JDK
uses: actions/setup-java@v5
with:
diff --git a/.github/workflows/check-semantic-pr.yml b/.github/workflows/check-semantic-pr.yml
index 57142ac..b0ce8b2 100644
--- a/.github/workflows/check-semantic-pr.yml
+++ b/.github/workflows/check-semantic-pr.yml
@@ -10,4 +10,4 @@ permissions:
jobs:
check:
- uses: reqstool/.github/.github/workflows/common-check-semantic-pr.yml@e1d67194373e4da7ccfdf400f46201f18ca14f23 # main 2026-03-07
+ uses: reqstool/.github/.github/workflows/common-check-semantic-pr.yml@main
diff --git a/.github/workflows/check_release.yml b/.github/workflows/check_release.yml
deleted file mode 100644
index 62fe017..0000000
--- a/.github/workflows/check_release.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-name: Check rules for release
-on:
- workflow_call:
-
-jobs:
- check-release:
- runs-on: ubuntu-latest
- steps:
- - name: Check branch and tag
- if: github.event_name == 'push' && !(github.ref == 'refs/heads/main' && startsWith(github.ref, 'refs/tags/'))
- run: exit 1
diff --git a/.github/workflows/publish_maven.yml b/.github/workflows/publish_maven.yml
deleted file mode 100644
index 96ea97e..0000000
--- a/.github/workflows/publish_maven.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-name: Publish package to the Maven Central Repository
-on:
- release:
- types: [created]
-
-jobs:
- check-release:
- name: Reuse check release
- uses: ./.github/workflows/check_release.yml
- build:
- name: Reuse build
- uses: ./.github/workflows/build.yml
- publish:
- needs:
- - build
- - check-release
- runs-on: ubuntu-latest
- permissions:
- contents: read
- packages: write
- steps:
- - uses: actions/checkout@v7
- - name: Set up Java for publishing to Maven Central Repository
- uses: actions/setup-java@v5
- with:
- java-version: "21"
- distribution: "temurin"
- server-id: central
- server-username: MAVEN_CENTRAL_USERNAME
- server-password: MAVEN_CENTRAL_TOKEN
- gpg-private-key: ${{ secrets.REQSTOOL_PRIVATE_GPG_KEY }}
- gpg-passphrase: REQSTOOL_PRIVATE_GPG_PASSPHRASE
- - name: Publish to the Maven Central Repository
- run: mvn clean deploy
- env:
- MAVEN_CENTRAL_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
- MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
- REQSTOOL_PRIVATE_GPG_PASSPHRASE: ${{ secrets.REQSTOOL_PRIVATE_GPG_PASSPHRASE }}
-
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..83c765d
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,114 @@
+name: Release
+
+# The whole release, start to finish. See RELEASING.md in reqstool/.github for
+# what each step does, and for why the release is created as a prerelease rather
+# than a draft.
+
+on:
+ workflow_dispatch:
+ inputs:
+ version:
+ description: "Version to release (Maven, no v prefix), e.g. 1.1.0. Leave empty to auto-detect from Conventional Commits."
+ required: false
+ type: string
+ prerelease:
+ description: "Publish as a release candidate instead of a release: verified like any release, but never promoted to latest. The number is chosen for you (1.1.0 -> 1.1.0-rc1, then the next)."
+ required: false
+ type: choice
+ options: [none, rc, b, a]
+ default: none
+ ref:
+ description: "Branch to release from. Leave empty for the branch this workflow was dispatched on."
+ required: false
+ type: string
+ force:
+ description: "Allow a version that disagrees with the auto-detected one."
+ required: false
+ type: boolean
+ default: false
+ dry-run:
+ description: "Validate and preview only -- nothing tagged, nothing published."
+ required: false
+ type: boolean
+ default: true
+
+concurrency:
+ group: release
+ cancel-in-progress: false
+
+permissions:
+ contents: read
+
+jobs:
+ prepare:
+ uses: reqstool/.github/.github/workflows/common-release-prepare.yml@main
+ permissions:
+ contents: read
+ with:
+ version-format: maven
+ version: ${{ inputs.version }}
+ prerelease: ${{ inputs.prerelease }}
+ ref: ${{ inputs.ref }}
+ force: ${{ inputs.force }}
+ dry-run: ${{ inputs.dry-run }}
+
+ # The same checks that guard main, called rather than reimplemented, and run
+ # before the approval gate so the reviewer approves something already green
+ # rather than a version string.
+ checks:
+ needs: prepare
+ if: ${{ !inputs.dry-run }}
+ uses: ./.github/workflows/build.yml
+ permissions:
+ contents: read
+
+ # THE APPROVAL GATE -- bound to the `stable` environment, so it sits pending
+ # until a required reviewer approves it on the run page.
+ tag:
+ needs: [prepare, checks]
+ if: ${{ !inputs.dry-run }}
+ uses: reqstool/.github/.github/workflows/common-release-tag.yml@main
+ permissions:
+ contents: write
+ with:
+ version: ${{ needs.prepare.outputs.version }}
+ version-format: maven
+ ref: ${{ inputs.ref }}
+
+ # `mvn deploy` builds from the tag itself, so there is no separate build step:
+ # the artifacts it signs and uploads are the ones Nisse stamped from the tag.
+ # `version` makes a disagreement a hard stop before anything reaches Central.
+ publish-to-maven-central:
+ needs: [prepare, tag]
+ uses: reqstool/.github/.github/workflows/java-publish-to-maven.yml@main
+ permissions:
+ contents: read
+ packages: write
+ secrets: inherit
+ with:
+ ref: ${{ needs.prepare.outputs.version }}
+ version: ${{ needs.prepare.outputs.version }}
+ environment: stable
+
+ # Last, deliberately. Everything above can fail, and until this runs nothing
+ # resolving "the latest release" can see what was built -- the release is still
+ # a prerelease. Promotion itself is one API call against a release that already
+ # has its artifacts.
+ #
+ # The guard is `no job failed`, not the default `every job succeeded`: a release
+ # candidate deliberately skips the publish jobs that a real release runs, and a
+ # skipped dependency would otherwise cascade and skip this too -- leaving the
+ # candidate unpromoted, which is right, and every *real* release unpromoted the
+ # moment any optional job is skipped, which is not.
+ #
+ # `!inputs.dry-run` has to be spelled out for the same reason: on a dry run
+ # every job above is skipped, and "nothing failed" would otherwise be true.
+ promote:
+ needs: [prepare, publish-to-maven-central]
+ if: ${{ !inputs.dry-run && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }}
+ uses: reqstool/.github/.github/workflows/common-release-promote.yml@main
+ permissions:
+ contents: write
+ with:
+ version: ${{ needs.prepare.outputs.version }}
+ prerelease: ${{ needs.prepare.outputs.prerelease == 'true' }}
diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml
new file mode 100644
index 0000000..6787906
--- /dev/null
+++ b/.mvn/extensions.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ eu.maveniverse.maven.nisse
+ extension
+ 0.9.5
+
+
diff --git a/.mvn/maven.config b/.mvn/maven.config
new file mode 100644
index 0000000..7dc73fb
--- /dev/null
+++ b/.mvn/maven.config
@@ -0,0 +1 @@
+-Dnisse.source.jgit.dynamicVersion=true
diff --git a/pom.xml b/pom.xml
index 9ee1ec2..fdc900b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,7 +8,8 @@
io.github.reqstool
reqstool-maven-plugin
maven-plugin
- 1.0.4
+
+ ${nisse.jgit.dynamicVersion}
${project.artifactId}
Reqstool - Maven Plugin