Extract Gradle Plugin Portal publish into a separate dispatchable workflow#231
Conversation
Isolates Portal publication so a re-run never re-triggers the Maven Central step. Adds workflow_dispatch with required ref/version inputs so the v1.0.0 tag can be published retroactively. Root cause fixed: the missing GPG signing env (ORG_GRADLE_PROJECT_signingInMemoryKey*) is now present alongside Portal creds, resolving the signatory error on marker publications.
Review Summary by QodoExtract Gradle Plugin Portal publish into separate workflow
WalkthroughsDescription• Extracts Gradle Plugin Portal publishing into dedicated workflow • Adds workflow_dispatch for manual re-publishing with custom ref/version • Includes GPG signing environment variables for marker publication signing • Prevents duplicate Maven Central uploads on Portal re-runs Diagramflowchart LR
A["Version Tag Push"] --> B["publish-plugin-portal.yml"]
C["workflow_dispatch"] --> B
B --> D["Checkout ref"]
D --> E["Determine version"]
E --> F["publishPlugins with GPG env"]
F --> G["Gradle Plugin Portal"]
File Changes1. .github/workflows/publish-plugin-portal.yml
|
Code Review by Qodo
1. Ref/version not validated
|
There was a problem hiding this comment.
Pull request overview
This PR introduces a dedicated GitHub Actions workflow for publishing the :featured-gradle-plugin to the Gradle Plugin Portal, decoupling it from the Maven Central publishing workflow so Portal publishes can be re-run independently.
Changes:
- Added a new dispatchable workflow that publishes to the Gradle Plugin Portal on version tags and via
workflow_dispatch. - Implemented version resolution from either manual
workflow_dispatchinput or the pushed tag name. - Ensured both Plugin Portal credentials and GPG signing environment variables are provided for
publishPlugins.
| tags: | ||
| - "v[0-9]+.[0-9]+.[0-9]+" | ||
| - "v[0-9]+.[0-9]+.[0-9]+-*" |
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: false |
| if [[ -n "${INPUT_VERSION}" ]]; then | ||
| # Manual dispatch: use the explicitly supplied version. | ||
| VERSION="${INPUT_VERSION}" | ||
| else |
| workflow_dispatch: | ||
| inputs: | ||
| ref: | ||
| description: "Git ref to check out (e.g. v1.0.0). Must point to the exact release tag." | ||
| required: true | ||
| version: | ||
| description: "Plugin version to publish (e.g. 1.0.0, without leading v)." | ||
| required: true | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| publish-plugin-portal: | ||
| name: Publish plugin to Gradle Plugin Portal | ||
| runs-on: ubuntu-latest | ||
| environment: Main | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v6 | ||
| with: | ||
| # On tag push: check out the triggering tag. | ||
| # On workflow_dispatch: check out the explicit ref input so develop-HEAD | ||
| # (1.1.0-SNAPSHOT) is never published in place of the release tag. | ||
| ref: ${{ inputs.ref || github.ref }} | ||
|
|
||
| - uses: ./.github/actions/setup-build-env | ||
|
|
||
| - name: Determine version | ||
| id: version | ||
| env: | ||
| INPUT_VERSION: ${{ inputs.version }} | ||
| run: | | ||
| if [[ -n "${INPUT_VERSION}" ]]; then | ||
| # Manual dispatch: use the explicitly supplied version. | ||
| VERSION="${INPUT_VERSION}" | ||
| else | ||
| # Tag push: strip the leading "v" from the tag (e.g. v1.0.0 -> 1.0.0). | ||
| VERSION="${GITHUB_REF_NAME#v}" | ||
| fi | ||
| echo "VERSION_NAME=${VERSION}" | tee -a "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Publish plugin to Gradle Plugin Portal | ||
| # publishPlugins uploads directly — no manual promotion step unlike Maven Central. | ||
| # GPG signing env is required: com.gradle.plugin-publish creates maven publications | ||
| # for plugin markers and the signing plugin signs them as part of publishPlugins. | ||
| env: | ||
| GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }} | ||
| GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }} | ||
| ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_SIGNING_KEY }} | ||
| ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.GPG_KEY_ID }} | ||
| ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_KEY_PASSWORD }} | ||
| ORG_GRADLE_PROJECT_VERSION_NAME: ${{ steps.version.outputs.VERSION_NAME }} | ||
| run: ./gradlew --no-daemon :featured-gradle-plugin:publishPlugins --no-configuration-cache |
There was a problem hiding this comment.
1. Ref/version not validated 🐞 Bug ≡ Correctness
For workflow_dispatch, the workflow checks out inputs.ref but always publishes the explicitly provided inputs.version as ORG_GRADLE_PROJECT_VERSION_NAME without verifying they match. This can publish a version number that doesn’t correspond to the source being built, creating incorrect artifacts in the Gradle Plugin Portal.
Agent Prompt
## Issue description
`workflow_dispatch` accepts both `ref` and `version`, but the workflow never verifies that the checked-out ref corresponds to the version being published.
## Issue Context
The checkout step uses `ref: ${{ inputs.ref || github.ref }}` while `Determine version` uses `inputs.version` when present, and `Publish` exports `ORG_GRADLE_PROJECT_VERSION_NAME` from that computed version.
## Fix Focus Areas
- .github/workflows/publish-plugin-portal.yml[31-66]
## Suggested fix
Add a validation step for `workflow_dispatch` before publishing, e.g.:
- Require `inputs.ref` to be a tag of the form `v${inputs.version}` (string compare).
- Optionally verify it is an exact tag checkout: `git describe --tags --exact-match`.
- Alternatively, remove `version` input entirely and derive version strictly from `inputs.ref` (strip leading `v`) to eliminate mismatch risk.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
What changed
Adds
.github/workflows/publish-plugin-portal.yml— a dedicated workflow that publishes:featured-gradle-pluginto the Gradle Plugin Portal, separate from the Maven Central job inpublish.yml.v*) andworkflow_dispatchwith requiredref+versioninputs.workflow_dispatchchecks out the suppliedref(the release tag), so an already-cut release can be (re)published to the Portal without re-running the Maven Central upload.GRADLE_PUBLISH_KEY/SECRET) and the GPG signing env (signingInMemoryKey*).Why
The v1.0.0 publish run failed at
signFeaturedApplicationPluginMarkerMavenPublication— "no configured signatory".com.gradle.plugin-publishcreates Maven publications for the plugin markers; thesigningplugin signs them, sopublishPluginsdepends on the sign tasks. The inline Portal step lacked the GPG env (only the Maven Central step had it). The Maven Central upload itself succeeded — only Portal publishing was blocked.Splitting Portal into its own workflow means a re-run never re-triggers the already-successful
publishToMavenCentralstep (no risk of a duplicate Central staging repo).Note for reviewers
workflow_dispatchis only exposed once this file reaches the default branch (main). Until then, dispatching fails with "workflow not found"; the tag-push path works for future releases regardless. Publishing the current 1.0.0 to the Portal therefore happens out-of-band (localpublishPluginsfrom thev1.0.0tag) — tracked separately.versionis passed via theINPUT_VERSIONenv var rather than inline shell interpolation (injection-safe).See
swarm-report/v1-portal-publish-state.md.🤖 Generated with Claude Code