Skip to content

Latest commit

 

History

History
199 lines (139 loc) · 10.3 KB

File metadata and controls

199 lines (139 loc) · 10.3 KB

Versioning, ABI, and release policy

Authoritative tooling per ADR-003: axion-release-plugin (git-tag-driven SemVer), Kotlin's built-in klib ABI validation (Kotlin 2.2+, formerly the standalone binary-compatibility-validator), Vanniktech maven-publish-plugin publishing direct to Sonatype Central Portal (no OSSRH staging dance).

SemVer

MAJOR.MINOR.PATCH, derived from the most recent annotated git tag (vX.Y.Z):

Bump When
MAJOR Source- or binary-incompatible change to :core or any storage interface. Adding a new sealed-class subtype (forces consumer when exhaustiveness break). Bumping the proto submodule MAJOR (rare).
MINOR New public API; new transport module; bumping the proto submodule MINOR (most common — new PortNum, new Config field). New optional Builder method.
PATCH Bug fix; internal refactor; doc-only change; bumping the proto submodule PATCH (additive proto field with default value).

Snapshot builds (X.Y.Z-SNAPSHOT) publish on every push to main to the Central Portal snapshot repository; tagged releases publish to Maven Central via manual workflow dispatch.

Pre-1.0 policy

While version is 0.x.y:

  1. Breaking changes are allowed between any two MINOR versions (0.1.x → 0.2.0).
  2. Every breaking change MUST:
    • Bump MINOR.
    • Regenerate api/ files via ./gradlew updateKotlinAbi in the same commit.
    • Add a ## [0.MINOR.0] section to CHANGELOG.md with ### Breaking subsection.
    • Be called out in the GitHub release notes as **BREAKING**.
  3. Deprecation is optional but encouraged for changes a consumer can adapt to: mark with @Deprecated(level = WARNING) for one MINOR, then remove in the next.
  4. Kotlin's built-in klib ABI validation runs in PR CI as hard-gate from Phase 0 (matches mqtt-client's day-1 enforcement). A PR that changes api/*.api MUST commit the regenerated dump.

1.0+ policy

From 1.0.0:

  1. No breaking change to public API in any non-MAJOR release. Period.
  2. ABI validation continues as hard-gate; an api/*.api change without a MAJOR-bump label fails CI.
  3. Removal cycle: deprecate at version N.M, remove at version (N+1).0 (next MAJOR).
  4. Sealed-class additions count as breaking for consumers using exhaustive when — they ship in MAJOR releases only. Code review flags any new sealed-class case in a :core PR (no automated rule covers this; reviewers consult ADR-005).
  5. Adding a new throwable subclass to MeshtasticException is breaking for the same reason. New error categories ship as MAJOR.

ABI tooling

Kotlin 2.2 brings klib ABI validation into KGP itself, deprecating the standalone kotlinx-binary-compatibility-validator plugin. We use the built-in mechanism:

  • ./gradlew checkKotlinAbi — fails if api/*.api differs from the current public surface.
  • ./gradlew updateKotlinAbi — regenerate; commit the result.
  • Per-target klibApiCheck runs against each Kotlin/Native and Kotlin/Wasm target's effective ABI, in addition to the JVM checkKotlinAbi.
  • Configuration lives in build-logic/convention/MeshtasticKmpPublishPlugin.kt (one place, applied to every published module).

:proto exemption

:proto is excluded from ABI validation. Reason:

  • :proto is mechanically generated by Wire from the vendored meshtastic/protobufs submodule.
  • Every proto bump regenerates symbols; the diff is, by design, the change.
  • We track proto bumps via the SemVer rules in the next section, not via line-by-line ABI gates.
  • A consumer's reaction to a proto change is governed by Wire's own source/ABI rules, not by ours.

checkKotlinAbi is therefore disabled on :proto (configured in MeshtasticKmpPublishPlugin with apiValidation { ignoredProjects += "proto" } or equivalent in the new KGP config). The Gradle dep graph plus :core:verifyModuleBoundary enforces that :core does not re-export proto types beyond what api(project(":proto")) already exposes — the proto surface remains stable in one place (Wire), not two.

Proto submodule policy

proto/src/protobufs is a git submodule pointing at meshtastic/protobufs. Bumping it is the integration point for every new firmware feature.

Upstream proto change This SDK's bump
New optional field on existing message PATCH
New PortNum value MINOR (new app payload becomes routable)
New Config.* / ModuleConfig.* substructure MINOR
Sealed oneof extension MINOR
Field removal / type change MAJOR
Renumbered enum value MAJOR
Top-level message added (e.g., new FromRadio arm) MINOR (Wire generates a sealed-class case in FromRadio.body; consumers' exhaustive whens break — but FromRadio is internal/transient enough that we accept this at MINOR pre-1.0; after 1.0 this also escalates to MAJOR)

The proto bump commit MUST:

  • Update the submodule pointer.
  • Run ./gradlew updateKotlinAbi to capture the new generated symbols on :core (the BCV-relevant surface). :proto's own dump is not part of the gate.
  • Note the upstream commit range in the bump commit body.
  • If a wire message has a new oneof arm, audit WireCodec for places that exhaustively when over it.

Renovate's git-submodules manager opens a PR weekly with the latest submodule pointer; a maintainer reviews and merges. See ../renovate.json.

Release workflow

Tagging

git tag -a v0.5.0 -m "Release 0.5.0"
git push --tags

axion-release reads the tag; the resulting ./gradlew currentVersion returns 0.5.0.

Publishing

Vanniktech publishes direct to the Sonatype Central Portal in one shot — there is no separate staging-repository "close-and-release" step.

# Snapshots (any non-tag build):
./gradlew publishToMavenCentral --no-configuration-cache

# Tagged release:
./gradlew publishAndReleaseToMavenCentral --no-configuration-cache

Driven from a manual release.yml GitHub Actions workflow (workflow_dispatch) on the tagged commit. Required secrets (vanniktech-standard names):

  • MAVEN_CENTRAL_USERNAME — Central Portal token username.
  • MAVEN_CENTRAL_PASSWORD — Central Portal token password.
  • SIGNING_IN_MEMORY_KEY — ASCII-armored GPG private key (gpg --armor --export-secret-keys $KEY), newlines preserved.
  • SIGNING_IN_MEMORY_KEY_PASSWORD — passphrase for the above.

Artifacts published per release (MVP)

Coordinates Source module
org.meshtastic:sdk-bom :bom (Maven BOM aligning the versions below)
org.meshtastic:sdk-core :core
org.meshtastic:sdk-proto :proto
org.meshtastic:sdk-transport-ble :transport-ble
org.meshtastic:sdk-transport-tcp :transport-tcp
org.meshtastic:sdk-transport-serial :transport-serial (single KMP module covering JVM via jSerialComm and Android via usb-serial-for-android)
org.meshtastic:sdk-storage-sqldelight :storage-sqldelight
org.meshtastic:sdk-testing :testing

All modules ship at the same version. KMP variants (Android AAR, JVM JAR, iOS Klib + XCFramework via ADR-007) publish under the same coordinates with platform classifiers per Gradle convention.

Roadmap artifacts (sdk-rpc, sdk-transport-rpc, sdk-host-rpc-server, sdk-transport-mqtt-proxy) ship additively when the wasm/RPC roadmap lands; see ./future/wasm-rpc-roadmap.md.

BOM

:bom is a tiny Maven BOM module declaring <dependencyManagement> entries for every published artifact above at the current release version. Consumers can:

implementation(platform("org.meshtastic:sdk-bom:0.5.0"))
implementation("org.meshtastic:sdk-core")           // version comes from BOM
implementation("org.meshtastic:sdk-transport-ble")  // version comes from BOM

The BOM is itself versioned and bumped on every release (axion handles this uniformly).

Release notes

Generated from CHANGELOG.md ## [X.Y.Z] section + a manually-curated highlight blurb. Format:

## [0.5.0] — 2026-MM-DD

### Added
- ...

### Changed
- ...

### Breaking
- ...   (only pre-1.0; post-1.0 these only appear in MAJOR releases)

### Fixed
- ...

### Proto
- Bumped `meshtastic/protobufs` submodule to <sha> (range <prev>..<sha>).

Branching

  • main — always green; tags are cut from here.
  • release/X.Y — long-lived once we ship 1.0, for back-port patch releases. Pre-1.0 only main exists.
  • Feature branches are short-lived; rebase + squash-merge into main (no merge commits in main's linear history).

Compatibility matrix

Documented in docs/compatibility.md (TBD) and the README:

SDK version Proto submodule Min firmware (capability-gated) Kotlin Gradle Min Android SDK iOS deployment
0.x.y (varies; commit pin in CHANGELOG) No hard minimum; capabilities gate on proto fields (e.g. DeviceMetadata.hasPKC for PKI, ClientNotification variants for security warnings). Practical floor tracks the pinned submodule — currently ≈ firmware 2.6.x. latest stable at release 9.x 26 iOS 14

The SDK is forward-compatible for additive proto fields and does not hard-fail on firmware-version mismatch today. MeshtasticException.FirmwareTooOld is reserved for a future opt-in check (see docs/error-taxonomy.md). docs/compatibility.md will be added when a real hard minimum is enforced.

Yanking a release

If a release ships a critical defect:

  1. Open a hotfix PR; bump PATCH.
  2. Cut a new tag immediately.
  3. The bad version is not unpublished from Maven Central (Central does not support unpublish). Instead, the CHANGELOG marks it ## [0.5.0] — YANKED with a pointer to 0.5.1 and a brief reason.
  4. README installation snippets always show the latest non-yanked version.

Related