Skip to content

ci(release): add Maven Central idempotency guard to publish step#56

Merged
jamesarich merged 1 commit into
mainfrom
ci/release-maven-central-idempotency-guard
Jul 17, 2026
Merged

ci(release): add Maven Central idempotency guard to publish step#56
jamesarich merged 1 commit into
mainfrom
ci/release-maven-central-idempotency-guard

Conversation

@jamesarich

@jamesarich jamesarich commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a Check Maven Central state step to release.yml that probes repo1.maven.org for the version's sdk-core POM and guards the publish step with the resulting already_published output, so a plain re-run after a partial failure skips the already-done upload instead of failing on Sonatype component already exists. Ports the pattern from TAKPacket-SDK's release workflow.

Type of change

  • Infrastructure / CI / build
  • Documentation only (release-runbook note)

Related issue / discussion

Fixes #55

Affirmations

  • All commits are signed off (DCO — git commit -s).
  • I have read CONTRIBUTING.md.
  • I have run ./gradlew check locally and it passes. (N/A — no Kotlin/Gradle sources touched; workflow + docs only.)

How was this verified?

  • actionlint and YAML parse pass on the edited workflow.
  • The probe script was exercised locally under set -e for all three cases:
    • existing artifact → HTTP 200 → already_published=true (publish skips)
    • missing version → HTTP 404 → already_published=false (publish runs)
    • unreachable host → curl fails, || true catches it, HTTP 000 → already_published=false (publish runs; step does not die)

Notes for reviewers

  • Probe coordinate: org/meshtastic/sdk-core/$VERSION/sdk-core-$VERSION.pom. All sdk-* artifacts (core, transports, storage, testing, bom) publish in one Central Portal deployment via vanniktech publishAndReleaseToMavenCentral, so sdk-core's POM is an accurate proxy for the whole release — either the deployment landed or it didn't.
  • Fail-open on probe errors: a transient probe failure is treated as "not published" and the publish is attempted — worst case is exactly today's behavior.
  • Sync-lag caveat (documented in the runbook): a re-run started within Sonatype→repo1's ~10–25 min sync window can still hit component already exists; the runbook says to wait for sync and re-run. Same caveat exists in the TAKPacket original.
  • The guard composes with dry-run: if: ${{ inputs.dry_run != true && steps.mc_check.outputs.already_published == 'false' }}.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved release publishing to avoid attempting duplicate Maven Central uploads.
    • Re-running interrupted releases is now safer and more reliable.
  • Documentation

    • Added guidance for handling partial release failures, repository synchronization delays, and verifying publication before retrying.

Probe repo1.maven.org for this version's sdk-core POM before publishing
and skip the upload when it is already there, so a re-run after a
partial failure (e.g. the workflow dies after a successful publish, as
happened to sibling repos during the 2026-07-16 Actions outage) recovers
cleanly instead of failing on Sonatype 'component already exists'.

Ports TAKPacket-SDK's 'Check Maven Central state' pattern. sdk-core is
the probe coordinate because all sdk-* artifacts publish together in a
single Central Portal deployment. A transient probe failure degrades to
'not published' (attempt the publish), which is today's behavior.

Also documents the re-run recovery path in the release runbook.

Fixes #55

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The release workflow now probes Maven Central for the target sdk-core POM and skips publication when it already exists. The release runbook documents safe reruns, repository synchronization delays, and verification steps.

Changes

Maven Central idempotency

Layer / File(s) Summary
Publish guard and rerun documentation
.github/workflows/release.yml, docs/release-runbook.md
The workflow sets already_published from a Maven Central probe and gates publication on that result; the runbook documents partial-failure reruns and synchronization timing.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

  • meshtastic/MQTTastic-Client-KMP#82 — The changes implement the same Maven Central existence check and guarded publish recovery objective.

Poem

A rabbit checks the Maven gate,
And skips a duplicate fate.
If runners hop and workflows stall,
Wait for Central’s sync call.
Then safely thump “rerun” twice—
No component exists surprise!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding a Maven Central idempotency guard to the release publish step.
Linked Issues check ✅ Passed The workflow adds the repo1.maven.org probe, exposes already_published, and skips publish on reruns as required by #55.
Out of Scope Changes check ✅ Passed The docs update supports the release recovery flow and caveat described in the issue, so no unrelated changes stand out.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 95-97: Update the mc_check step to pass steps.ver.outputs.version
through an env variable and reference that variable in the shell instead of
interpolating it in run. Apply the same safe environment-variable pattern to the
unchanged Resolve version step for inputs.version, preserving the existing
version-resolution behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 282f30c2-5c26-4050-b374-7d65e2b078b5

📥 Commits

Reviewing files that changed from the base of the PR and between 55ae2c8 and e292b71.

📒 Files selected for processing (2)
  • .github/workflows/release.yml
  • docs/release-runbook.md

Comment on lines +95 to +97
id: mc_check
run: |
VERSION="${{ steps.ver.outputs.version }}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Prevent code injection via template expansion.

Expanding the expression ${{ steps.ver.outputs.version }} directly into the run script leaves the workflow vulnerable to command injection if the version string contains malicious input. Instead, bind the context value to an environment variable and reference it in the shell script.

(Note: Consider applying this same pattern to the unchanged "Resolve version" step above, which also expands inputs.version directly).

🔒️ Proposed fix
         id: mc_check
+        env:
+          VERSION: ${{ steps.ver.outputs.version }}
         run: |
-          VERSION="${{ steps.ver.outputs.version }}"
           # `|| true`: a transient probe failure must not kill the run —
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
id: mc_check
run: |
VERSION="${{ steps.ver.outputs.version }}"
id: mc_check
env:
VERSION: ${{ steps.ver.outputs.version }}
run: |
🧰 Tools
🪛 zizmor (1.26.1)

[info] 97-97: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 95 - 97, Update the mc_check step
to pass steps.ver.outputs.version through an env variable and reference that
variable in the shell instead of interpolating it in run. Apply the same safe
environment-variable pattern to the unchanged Resolve version step for
inputs.version, preserving the existing version-resolution behavior.

Source: Linters/SAST tools

@jamesarich
jamesarich merged commit 7e96cf9 into main Jul 17, 2026
11 checks passed
@jamesarich
jamesarich deleted the ci/release-maven-central-idempotency-guard branch July 17, 2026 00:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Release workflow: add Maven Central idempotency guard

1 participant