mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-05-11 16:15:24 -04:00
chore(release): add release-please pilot and versioning standards
- docs/versioning.md: full versionCode/versionName policy, multi-track monotonicity rationale, release-please SemVer pilot decision record - version.txt: release-please primary tracking file (2.7.14) - CHANGELOG.md: bootstrap file to be maintained by release-please - .github/release-please-config.json: simple release strategy, bootstrap-sha, extra-files for config.properties, changelog-sections mapping - .release-please-manifest.json: initial version manifest - .github/workflows/release-please.yml: non-shipping pilot, runs on main push, creates/updates Release PR and posts step summary with next actions - config.properties: add x-release-please block markers around VERSION_NAME_BASE - promote.yml: idempotent production tag push (skip if tag already exists) - RELEASE_PROCESS.md: add Step 0 (release-please flow) and versioning doc link Agent-Logs-Url: https://github.com/meshtastic/Meshtastic-Android/sessions/51656e75-0d04-4854-a033-a7064f9f88fb Co-authored-by: jamesarich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
228d872f9d
commit
1896970d71
32
.github/release-please-config.json
vendored
Normal file
32
.github/release-please-config.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"bootstrap-sha": "228d872f9d460173d794b37f49cf4ac042d9826e",
|
||||
"packages": {
|
||||
".": {
|
||||
"release-type": "simple",
|
||||
"changelog-path": "CHANGELOG.md",
|
||||
"include-v-in-tag": true,
|
||||
"tag-separator": "",
|
||||
"draft": false,
|
||||
"prerelease": false,
|
||||
"bump-minor-pre-major": false,
|
||||
"bump-patch-for-minor-pre-major": false,
|
||||
"extra-files": [
|
||||
{
|
||||
"type": "generic",
|
||||
"path": "config.properties"
|
||||
}
|
||||
],
|
||||
"changelog-sections": [
|
||||
{"type": "feat", "section": "🏗️ Features"},
|
||||
{"type": "fix", "section": "🛠️ Fixes"},
|
||||
{"type": "deps", "section": "📦 Dependencies"},
|
||||
{"type": "refactor", "section": "♻️ Refactors", "hidden": true},
|
||||
{"type": "chore", "section": "🔧 Chores", "hidden": true},
|
||||
{"type": "docs", "section": "📝 Docs", "hidden": true},
|
||||
{"type": "test", "section": "🧪 Tests", "hidden": true},
|
||||
{"type": "build", "section": "🏗 Build", "hidden": true},
|
||||
{"type": "ci", "section": "⚙️ CI", "hidden": true}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
13
.github/workflows/promote.yml
vendored
13
.github/workflows/promote.yml
vendored
@@ -129,8 +129,17 @@ jobs:
|
||||
- name: Push Git Tag on Success
|
||||
if: ${{ inputs.commit_sha != '' }}
|
||||
run: |
|
||||
git tag ${{ inputs.final_tag }} ${{ inputs.commit_sha }}
|
||||
git push origin ${{ inputs.final_tag }}
|
||||
# Create the tag only if it does not already exist on the remote.
|
||||
# release-please may have already created the production tag (vX.Y.Z)
|
||||
# when its Release PR was merged, so we check first to avoid failing.
|
||||
FINAL_TAG="${{ inputs.final_tag }}"
|
||||
COMMIT_SHA="${{ inputs.commit_sha }}"
|
||||
if git ls-remote --tags origin "$FINAL_TAG" | grep -q "$FINAL_TAG"; then
|
||||
echo "Tag $FINAL_TAG already exists on remote (likely created by release-please). Skipping tag push."
|
||||
else
|
||||
git tag "$FINAL_TAG" "$COMMIT_SHA"
|
||||
git push origin "$FINAL_TAG"
|
||||
fi
|
||||
|
||||
- name: Update GitHub Release with gh CLI
|
||||
env:
|
||||
|
||||
60
.github/workflows/release-please.yml
vendored
Normal file
60
.github/workflows/release-please.yml
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
name: Release Please
|
||||
|
||||
# Runs on every push to main. release-please:
|
||||
# 1. Parses Conventional Commit messages since the last release.
|
||||
# 2. Opens (or updates) a Release PR proposing the next SemVer bump and updating
|
||||
# CHANGELOG.md, version.txt, and VERSION_NAME_BASE in config.properties.
|
||||
# 3. When the Release PR is merged, creates a `vX.Y.Z` tag and a draft GitHub Release.
|
||||
#
|
||||
# The existing channel-promotion pipeline (Create or Promote Release) remains the
|
||||
# authoritative deployment path. See docs/versioning.md for the full decision record.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: release-please-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
release-please:
|
||||
name: Release Please
|
||||
runs-on: ubuntu-24.04-arm
|
||||
steps:
|
||||
- uses: googleapis/release-please-action@v4
|
||||
id: release
|
||||
with:
|
||||
# A PAT is required so that release-please PRs and the resulting
|
||||
# release tag/commit trigger follow-on CI workflows (the built-in
|
||||
# GITHUB_TOKEN does not trigger workflows on its own events).
|
||||
token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
config-file: .github/release-please-config.json
|
||||
manifest-file: .release-please-manifest.json
|
||||
|
||||
# ── Informational output ────────────────────────────────────────────────
|
||||
- name: Print release-please outputs
|
||||
if: always()
|
||||
env:
|
||||
RELEASE_CREATED: ${{ steps.release.outputs.release_created }}
|
||||
TAG_NAME: ${{ steps.release.outputs.tag_name }}
|
||||
VERSION: ${{ steps.release.outputs.version }}
|
||||
PR_NUMBER: ${{ steps.release.outputs.pr }}
|
||||
run: |
|
||||
echo "### release-please summary" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "" >> "$GITHUB_STEP_SUMMARY"
|
||||
if [[ "$RELEASE_CREATED" == "true" ]]; then
|
||||
echo "✅ **Release created:** \`$TAG_NAME\` (v$VERSION)" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "Next steps:" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "1. Run the **Create or Promote Release** workflow with \`base_version: $VERSION\` and \`channel: internal\` to start the deployment pipeline." >> "$GITHUB_STEP_SUMMARY"
|
||||
elif [[ -n "$PR_NUMBER" ]]; then
|
||||
echo "🔄 **Release PR updated:** #$PR_NUMBER" >> "$GITHUB_STEP_SUMMARY"
|
||||
else
|
||||
echo "ℹ️ No releasable commits found since last release — no PR created." >> "$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
3
.release-please-manifest.json
Normal file
3
.release-please-manifest.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
".": "2.7.14"
|
||||
}
|
||||
10
CHANGELOG.md
Normal file
10
CHANGELOG.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to Meshtastic-Android are documented here.
|
||||
|
||||
This file is maintained automatically by
|
||||
[release-please](https://github.com/googleapis/release-please).
|
||||
Do not edit it by hand — open a conventional-commit PR and the next
|
||||
Release PR will include your changes automatically.
|
||||
|
||||
See [RELEASE_PROCESS.md](RELEASE_PROCESS.md) for the full release workflow.
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
This guide summarizes the steps for releasing a new version of Meshtastic-Android. The process is fully automated via GitHub Actions and Fastlane.
|
||||
|
||||
For the versioning policy (`versionCode` formula, SemVer bump rules, and the release-please integration), see [docs/versioning.md](docs/versioning.md).
|
||||
|
||||
## Overview
|
||||
|
||||
The entire release process is managed by a single, manually-triggered GitHub Action: **`Create or Promote Release`**.
|
||||
@@ -20,6 +22,25 @@ The entire release process is managed by a single, manually-triggered GitHub Act
|
||||
|
||||
## Release Steps
|
||||
|
||||
### 0. Prepare the release (release-please)
|
||||
|
||||
The repository runs a **release-please** pilot that automatically maintains a **Release PR** on
|
||||
`main`. This PR proposes the next SemVer version bump (determined from
|
||||
[Conventional Commit](https://www.conventionalcommits.org/) prefixes) and updates:
|
||||
- `CHANGELOG.md`
|
||||
- `version.txt`
|
||||
- `VERSION_NAME_BASE` in `config.properties`
|
||||
|
||||
When the team is ready to cut a new version:
|
||||
|
||||
1. Review the open Release PR (titled `chore(main): release X.Y.Z`) in the repository.
|
||||
2. Make any changelog edits inside the PR using the `BEGIN_COMMIT_OVERRIDE` / `END_COMMIT_OVERRIDE` mechanism (see [docs/versioning.md](docs/versioning.md)).
|
||||
3. **Merge the Release PR.** release-please will tag the commit as `vX.Y.Z` and open a draft GitHub Release.
|
||||
4. Proceed with the internal build (Step 1 below) using the newly bumped version.
|
||||
|
||||
> If you need to force a specific version, push an empty commit with `Release-As: x.y.z` in the
|
||||
> body before merging the Release PR.
|
||||
|
||||
### 1. Start an Internal Release
|
||||
|
||||
1. Navigate to the **Actions** tab in the GitHub repository.
|
||||
@@ -52,6 +73,10 @@ After testing is complete on all pre-release channels, you can create the final
|
||||
3. Select the `production` channel.
|
||||
4. The workflow will create a clean version tag (e.g., `v2.4.0`) and create a **published, stable** (non-prerelease) release on GitHub.
|
||||
|
||||
> **Note:** If the release-please Release PR was merged before this step, the `v2.4.0` tag
|
||||
> already exists (created by release-please). The production promotion workflow detects this
|
||||
> and skips the duplicate tag push gracefully.
|
||||
|
||||
### 4. Post-Release
|
||||
|
||||
1. **Verify:** Check the Google Play Console to ensure the build is available on the correct track.
|
||||
|
||||
@@ -24,10 +24,11 @@ MIN_SDK=26
|
||||
TARGET_SDK=37
|
||||
COMPILE_SDK=37
|
||||
|
||||
# Base version name for local development and fallback
|
||||
# On CI, this is overridden by the Git tag
|
||||
# Before a release, update this to the new Git tag version
|
||||
# Base version name for local development and fallback.
|
||||
# On CI this is overridden by the Git tag.
|
||||
# x-release-please-start-version
|
||||
VERSION_NAME_BASE=2.7.14
|
||||
# x-release-please-end-version
|
||||
|
||||
# Minimum firmware versions supported by this app
|
||||
MIN_FW_VERSION=2.5.14
|
||||
|
||||
140
docs/versioning.md
Normal file
140
docs/versioning.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# Meshtastic-Android Versioning Standards
|
||||
|
||||
This document describes how `versionCode` and `versionName` are computed for all Android and
|
||||
Desktop builds, and how the project integrates `release-please` for automated changelog and
|
||||
version-name management.
|
||||
|
||||
---
|
||||
|
||||
## 1. `versionCode` — canonical source of truth
|
||||
|
||||
### Formula
|
||||
|
||||
```
|
||||
versionCode = git rev-list --count HEAD + VERSION_CODE_OFFSET
|
||||
```
|
||||
|
||||
`VERSION_CODE_OFFSET` is stored in `config.properties` and is a one-time fixed integer large
|
||||
enough to ensure the resulting `versionCode` is always higher than any code published before
|
||||
this monotonic scheme was adopted.
|
||||
|
||||
### Why commit-count + offset?
|
||||
|
||||
| Property | Rationale |
|
||||
|---|---|
|
||||
| **Monotonically increasing** | Every merged commit increments the count, satisfying Google Play's strict "must never decrease" requirement across all tracks. |
|
||||
| **Deterministic** | Given a full-history clone and the offset constant, any machine reproduces the same value — no "who ran the CI" race. |
|
||||
| **CI-friendly** | `lint-check` computes it once and passes it via `VERSION_CODE` env-var to downstream jobs that use `fetch-depth: 1`, preserving speed. |
|
||||
| **Human-readable** | The value visually conveys how many commits exist in the repo, which is useful in support contexts. |
|
||||
|
||||
### Multi-track monotonicity
|
||||
|
||||
Google Play maintains a single `versionCode` pool across all release tracks (internal, alpha,
|
||||
beta, production). The commit-count formula guarantees global monotonicity because:
|
||||
|
||||
- Internal builds always point to the most recent commit on `main`.
|
||||
- Subsequent promotions (`internal → closed → open → production`) re-use the **same** build artifact
|
||||
and therefore the **same** `versionCode`.
|
||||
- No build produced from a later commit can have a lower code than one produced from an earlier
|
||||
commit, regardless of track.
|
||||
|
||||
### ABI-split `versionCode`
|
||||
|
||||
The project builds per-ABI APKs for F-Droid/IzzyOnDroid. Each ABI gets a unique code via the
|
||||
standard Android `splits.abi` mechanism, which uses `versionCodeOverride`:
|
||||
|
||||
| ABI | Multiplier |
|
||||
|---|---|
|
||||
| `armeabi-v7a` | base |
|
||||
| `arm64-v8a` | base + 1 |
|
||||
| `x86` | base + 2 |
|
||||
| `x86_64` | base + 3 |
|
||||
|
||||
### Requirements for CI correctness
|
||||
|
||||
`git rev-list --count HEAD` requires a **full-history** clone. Wherever `versionCode` is
|
||||
calculated (CI `lint-check` job, release workflow `prepare-build-info` job), the checkout
|
||||
**must** use `fetch-depth: 0`. Shallow clones produce incorrect counts and are explicitly
|
||||
rejected by `GitVersionValueSource`.
|
||||
|
||||
---
|
||||
|
||||
## 2. `versionName` — canonical source of truth
|
||||
|
||||
`versionName` follows **Semantic Versioning** (`MAJOR.MINOR.PATCH`).
|
||||
|
||||
| Context | Source |
|
||||
|---|---|
|
||||
| Local dev / debug builds | `VERSION_NAME_BASE` in `config.properties` |
|
||||
| CI PR / push builds | `VERSION_NAME_BASE` in `config.properties` |
|
||||
| Release builds | `VERSION_NAME` env-var injected by Fastlane / CI, derived from the Git tag (`v2.7.14` → `2.7.14`) |
|
||||
|
||||
The displayed version string for product flavors appends the versionCode and flavor name:
|
||||
|
||||
```
|
||||
2.7.14 (29314800) fdroid
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Updating `VERSION_NAME_BASE` — the release-please pilot
|
||||
|
||||
Starting with the 2.7.x series, the project runs `release-please` as a non-shipping assistant
|
||||
that proposes version bumps and maintains `CHANGELOG.md` automatically.
|
||||
|
||||
### How it works
|
||||
|
||||
1. Every push to `main` triggers `.github/workflows/release-please.yml`.
|
||||
2. `release-please` parses [Conventional Commit](https://www.conventionalcommits.org/) messages
|
||||
(`feat:`, `fix:`, `feat!:` / `BREAKING CHANGE`, etc.) and determines the next SemVer bump.
|
||||
3. It opens (or updates) a **Release PR** that:
|
||||
- Bumps `version.txt` to the next version.
|
||||
- Updates the `# x-release-please-start-version` block in `config.properties`
|
||||
(keeping `VERSION_NAME_BASE` in sync).
|
||||
- Prepends the new section to `CHANGELOG.md`.
|
||||
4. When the team is ready to ship, they **merge the Release PR** on GitHub.
|
||||
5. `release-please` then creates a `v{version}` Git tag and a draft GitHub Release.
|
||||
6. The team then runs the existing **`Create or Promote Release`** workflow using the new version
|
||||
as the `base_version` input to build and deploy to the channel pipeline.
|
||||
|
||||
> **Pilot mode:** During the pilot, `skip-github-release` is set to `false` so that a draft
|
||||
> release is created as an anchor for release-please's version tracking. The existing
|
||||
> channel-promotion pipeline is still the authoritative deployment path.
|
||||
|
||||
### Conventional Commit → SemVer mapping
|
||||
|
||||
| Commit prefix | SemVer bump |
|
||||
|---|---|
|
||||
| `fix:` | patch (`2.7.14 → 2.7.15`) |
|
||||
| `feat:` | minor (`2.7.x → 2.8.0`) |
|
||||
| `feat!:` / `BREAKING CHANGE:` | major (`2.x.y → 3.0.0`) |
|
||||
| `chore:`, `docs:`, `refactor:`, `test:`, `build:` | no bump (not releasable units) |
|
||||
|
||||
### Forcing a specific version
|
||||
|
||||
Add `Release-As: x.y.z` to the **commit body** of any commit on `main` to override the
|
||||
auto-computed version:
|
||||
|
||||
```
|
||||
git commit --allow-empty -m "chore: release 3.0.0" -m "Release-As: 3.0.0"
|
||||
```
|
||||
|
||||
### Config files
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `.github/release-please-config.json` | Strategy, extra-files, bootstrap SHA |
|
||||
| `.release-please-manifest.json` | Current version per package (updated on each release PR merge) |
|
||||
| `version.txt` | Primary version file tracked by the `simple` release strategy |
|
||||
| `CHANGELOG.md` | Auto-generated and maintained by release-please |
|
||||
|
||||
---
|
||||
|
||||
## 4. Decision record
|
||||
|
||||
| Decision | Rationale |
|
||||
|---|---|
|
||||
| **Commit-count + offset for `versionCode`**, not a manually bumped integer | Eliminates human error (forgetting to increment), makes codes deterministic across machines, and automatically handles hotfix branches. |
|
||||
| **`simple` release-please strategy** instead of `java` / `maven` | This is a Kotlin/Gradle repo; the `simple` strategy's `version.txt` is the least invasive primary file, while `extra-files` keeps `config.properties` in sync. |
|
||||
| **`skip-github-release: false`** during pilot | Needed so release-please can find its own tags on subsequent runs; the existing channel-promotion pipeline still owns deployment. |
|
||||
| **Keep existing channel-promotion pipeline** | Google Play's internal→alpha→beta→production promotion model doesn't map onto release-please's single-track model; the existing bespoke pipeline handles this correctly. |
|
||||
1
version.txt
Normal file
1
version.txt
Normal file
@@ -0,0 +1 @@
|
||||
2.7.14
|
||||
Reference in New Issue
Block a user