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