diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b2980f50c..a6f8dee9e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -93,7 +93,9 @@ jobs: gpg-passphrase: MAVEN_GPG_PASSPHRASE - name: Publish to Apache Maven Central - run: mvn deploy -P 'release,!default' -Dmaven.test.skip=true + run: >- + ./mvnw -B deploy -P 'release,!default,!examples-and-integration-tests' + -Dmaven.test.skip=true env: MAVEN_USERNAME: ${{ secrets.SONATYPE_MAVEN_REPOSITORY_USERNAME }} MAVEN_CENTRAL_TOKEN: ${{ secrets.SONATYPE_MAVEN_REPOSITORY_PASSWORD }} diff --git a/.github/workflows/test-release-build.yml b/.github/workflows/test-release-build.yml index 7edd109ab..cbc751432 100644 --- a/.github/workflows/test-release-build.yml +++ b/.github/workflows/test-release-build.yml @@ -27,6 +27,8 @@ jobs: with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + - name: Check release deployment configuration + run: python3 .mise/tasks/test_release_configuration.py - name: Build GitHub Pages run: mise run build-gh-pages env: diff --git a/.mise/tasks/build-release.sh b/.mise/tasks/build-release.sh index 620dca77a..30a124fcf 100755 --- a/.mise/tasks/build-release.sh +++ b/.mise/tasks/build-release.sh @@ -4,5 +4,5 @@ set -euo pipefail -mvn -B package -P 'release,!default,!examples-and-integration-tests' \ +./mvnw -B package -P 'release,!default,!examples-and-integration-tests' \ -Dmaven.test.skip=true -Dgpg.skip=true diff --git a/.mise/tasks/test_release_configuration.py b/.mise/tasks/test_release_configuration.py new file mode 100644 index 000000000..4375e6c2e --- /dev/null +++ b/.mise/tasks/test_release_configuration.py @@ -0,0 +1,34 @@ +"""Guard the release reactor configuration without credentials or publishing artifacts.""" + +import re +import shlex +import unittest +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] + + +def profiles(text): + """Read the single explicit Maven profile selection in a release command.""" + selections = re.findall(r"\s-P\s+('[^']*'|\"[^\"]*\"|[^\s]+)", text) + if len(selections) != 1: + raise AssertionError("Expected exactly one explicit Maven -P selection") + return set(shlex.split(selections[0])[0].split(",")) + + +class ReleaseConfigurationTest(unittest.TestCase): + def test_build_and_deploy_use_the_same_release_reactor(self): + build = (ROOT / ".mise/tasks/build-release.sh").read_text() + deploy = (ROOT / ".github/workflows/release.yml").read_text() + expected = {"release", "!default", "!examples-and-integration-tests"} + self.assertEqual(profiles(build), expected) + self.assertEqual(profiles(deploy), profiles(build)) + + def test_regression_detects_the_failed_release_configuration(self): + build = (ROOT / ".mise/tasks/build-release.sh").read_text() + failed_command = "mvn deploy -P 'release,!default' -Dmaven.test.skip=true" + self.assertNotEqual(profiles(failed_command), profiles(build)) + + +if __name__ == "__main__": + unittest.main() diff --git a/RELEASING.md b/RELEASING.md index c5ae0ed4b..6365ba31a 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -44,6 +44,36 @@ the benchmarks before merging the release PR: mise run update-benchmarks ``` +## Retrying a Failed Maven Central Deployment + +A GitHub release and a Maven Central publication are separate operations. +An immutable GitHub release does not prevent publishing artifacts from its +existing tag. Do not move the tag or rebuild the release from newer source. + +Before retrying, check the failed job's full log and the Sonatype Central +Deployments page. Maven reactor `SUCCESS` entries can mean artifacts were +only staged locally; look for an uploaded bundle and deployment ID. If a +deployment is already pending, inspect it before submitting another one. +Already published Maven Central versions cannot be overwritten. + +For a workflow-only fix, merge the correction to `main`, then dispatch the +updated workflow with the original release tag: + +```shell +gh workflow run release.yml --repo prometheus/client_java --ref main -f tag=v1.9.0 +``` + +Replace `v1.9.0` with the tag being recovered. The workflow comes from `main`, +but its checkout uses the supplied tag. Rerunning the original failed job +instead uses its original workflow and repeats the same configuration error. +Changes to scripts or POMs on `main` are not picked up by that tagged checkout; +those require a separate recovery plan or a new release. + +Keep the deployment profile selection aligned with `mise run build-release`: +examples, benchmarks, and integration tests must not enter the release reactor. +The Test Build Release workflow checks this configuration before building. +This check does not validate signing credentials or Sonatype availability. + ## If the Sonatype Central Token is Invalid The release workflow verifies the token before deploy. If it fails: