ci: merge-queue contention fixes, desktop builds on main, and 22.04-runner exit for desktop releases (#6174)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
James Rich
2026-07-09 14:05:51 -05:00
committed by GitHub
parent d4d5879fd3
commit 2f899ca424
6 changed files with 126 additions and 14 deletions

View File

@@ -15,12 +15,20 @@ concurrency:
cancel-in-progress: true
jobs:
# Every commit on main arrives via the merge queue, which already ran lint, tests,
# screenshot validation, and rb-check on this exact merge commit. Re-running them here
# would be pure duplication — this workflow builds the debug APKs for the snapshot
# release below (run_lint: false also skips screenshot-check and rb-check) plus the
# desktop distributables. Desktop packaging runs here post-merge rather than in the
# merge queue: :desktopApp:test in the queue's shard-app already covers compilation,
# and the 4-OS matrix (macos/windows queue times) would slow every merge.
validate-and-build:
if: github.repository == 'meshtastic/Meshtastic-Android'
uses: ./.github/workflows/reusable-check.yml
with:
run_lint: true
run_lint: false
run_unit_tests: false
run_desktop_builds: true
upload_artifacts: true
secrets: inherit
@@ -30,7 +38,10 @@ jobs:
# expire after 7 days).
publish-snapshot:
needs: validate-and-build
if: github.repository == 'meshtastic/Meshtastic-Android'
# !cancelled(): a desktop-matrix failure fails validate-and-build as a whole, but the
# Android APKs may still have built fine — attempt the snapshot regardless. If the
# APK build itself failed, the artifact download below fails and this job goes red.
if: github.repository == 'meshtastic/Meshtastic-Android' && !cancelled()
runs-on: ubuntu-24.04-arm
permissions:
contents: write

View File

@@ -7,13 +7,74 @@ on:
permissions:
contents: read
# Note: github.ref is unique per merge-group entry (gh-readonly-queue/main/pr-N-<sha>),
# so this group never dedupes across re-queues of the same PR — the cancel-superseded
# job below handles that explicitly.
concurrency:
group: build-mq-${{ github.ref }}
cancel-in-progress: true
jobs:
android-check:
# When a PR is re-queued (an entry ahead of it failed or was removed), GitHub creates a
# new merge group but does NOT cancel the workflow runs of the destroyed one. Those stale
# runs sit queued/running and starve the runner pool. Cancel any older merge-queue run
# for the same PR — only the newest merge group per PR is ever valid.
cancel-superseded:
name: Cancel Superseded Queue Runs
if: github.repository == 'meshtastic/Meshtastic-Android'
runs-on: ubuntu-24.04-arm
timeout-minutes: 5
permissions:
actions: write
steps:
- name: Cancel older merge-queue runs for the same PR
env:
GH_TOKEN: ${{ github.token }}
run: |
# github.ref_name: gh-readonly-queue/main/pr-<num>-<base-sha>
PR_PREFIX=$(echo "${{ github.ref_name }}" | grep -oE 'gh-readonly-queue/.+/pr-[0-9]+-')
if [ -z "$PR_PREFIX" ]; then
echo "Could not parse PR from ref '${{ github.ref_name }}'; skipping."
exit 0
fi
for status in queued in_progress; do
gh api "repos/${{ github.repository }}/actions/runs?event=merge_group&status=${status}&per_page=100" \
--jq ".workflow_runs[] | select(.head_branch | startswith(\"$PR_PREFIX\")) | select(.id < ${{ github.run_id }}) | .id"
done | sort -u | while read -r run_id; do
echo "Cancelling superseded run $run_id"
gh run cancel "$run_id" --repo "${{ github.repository }}" || true
done
# Docs-only queue entries (changelog updates, markdown fixes) cannot affect the build;
# skip the heavy pipeline for them. Anything outside docs/ and *.md runs full CI.
# Mirrors the paths-ignore list in main-check.yml.
check-changes:
name: Check Changes
if: github.repository == 'meshtastic/Meshtastic-Android'
runs-on: ubuntu-24.04-arm
timeout-minutes: 5
outputs:
android: ${{ steps.filter.outputs.android }}
steps:
- uses: actions/checkout@v7.0.0
with:
fetch-depth: 1
- name: Diff merge group against its base
id: filter
run: |
git fetch --depth=1 origin "${{ github.event.merge_group.base_sha }}"
changed=$(git diff --name-only "${{ github.event.merge_group.base_sha }}" "${{ github.event.merge_group.head_sha }}")
echo "Changed files:"
echo "$changed"
if echo "$changed" | grep -qvE '^docs/|\.md$|^$'; then
echo "android=true" >> "$GITHUB_OUTPUT"
else
echo "android=false" >> "$GITHUB_OUTPUT"
fi
android-check:
needs: check-changes
if: github.repository == 'meshtastic/Meshtastic-Android' && needs.check-changes.outputs.android == 'true'
uses: ./.github/workflows/reusable-check.yml
with:
run_lint: true
@@ -27,12 +88,17 @@ jobs:
timeout-minutes: 5
permissions: {}
needs:
- check-changes
- android-check
if: always()
steps:
- name: Check Workflow Status
run: |
if [[ "${{ needs.android-check.result }}" == "failure" || "${{ needs.android-check.result }}" == "cancelled" ]]; then
if [[ "${{ needs.check-changes.result }}" != "success" ]]; then
echo "::error::Change detection failed"
exit 1
fi
if [[ "${{ needs.check-changes.outputs.android }}" == "true" && ("${{ needs.android-check.result }}" == "failure" || "${{ needs.android-check.result }}" == "cancelled") ]]; then
echo "::error::Android Check failed"
exit 1
fi

View File

@@ -264,7 +264,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [macos-latest, windows-latest, ubuntu-22.04, ubuntu-22.04-arm]
os: [macos-latest, windows-latest, ubuntu-24.04, ubuntu-24.04-arm]
env:
GRADLE_CACHE_URL: ${{ secrets.GRADLE_CACHE_URL }}
GRADLE_CACHE_USERNAME: ${{ secrets.GRADLE_CACHE_USERNAME }}
@@ -286,7 +286,7 @@ jobs:
- name: Install dependencies for AppImage
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y libfuse2
run: sudo apt-get update && sudo apt-get install -y libfuse2t64
- name: Package Native Distributions
env:
@@ -308,6 +308,33 @@ jobs:
${{ contains(runner.os, 'macOS') && ':desktopApp:packageReleaseUberJarForCurrentOS' || '' }}
'-PaboutLibraries.release=true' --no-daemon
# jpackage computes the .deb Depends: line from the build host's package names.
# Ubuntu 24.04 records time_t64 transition names (libasound2t64, libpng16-16t64)
# that don't exist on Debian 12 Bookworm / Raspberry Pi OS, making the .deb
# uninstallable there (#5233 pinned ubuntu-22.04 for this; those runners retire
# 2027-04, brownouts from 2026-09). Strip the t64 suffix instead: Bookworm ships
# the plain names, and on Trixie / Ubuntu 24.04+ the t64 packages Provide them.
# Rewriting the control file is the only host-independent fix — jpackage's
# --linux-package-deps is additive-only, and the Compose plugin clears its
# --resource-dir during the task, so a custom control template can't be injected.
- name: Rewrite t64 dependency names in .deb (Debian Bookworm / Pi OS compatibility)
if: runner.os == 'Linux'
run: |
shopt -s nullglob
for deb in desktopApp/build/compose/binaries/main-release/deb/*.deb; do
work=$(mktemp -d)
dpkg-deb -R "$deb" "$work"
echo "$(basename "$deb") before: $(grep '^Depends:' "$work/DEBIAN/control")"
sed -i -E '/^(Depends|Pre-Depends|Recommends):/ s/(lib[a-z0-9.+-]*)t64([, (]|$)/\1\2/g' "$work/DEBIAN/control"
echo "$(basename "$deb") after: $(grep '^Depends:' "$work/DEBIAN/control")"
if grep -nE '^[A-Za-z-]+:.*\blib[a-z0-9.+-]*t64\b' "$work/DEBIAN/control"; then
echo "::error::t64 dependency name survived rewrite in $(basename "$deb") — extend the sed above"
exit 1
fi
dpkg-deb -b --root-owner-group "$work" "$deb"
rm -rf "$work"
done
- name: List Desktop Binaries
if: runner.os == 'Linux' || runner.os == 'macOS'
run: ls -R desktopApp/build/compose/binaries/main-release
@@ -348,7 +375,9 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, ubuntu-22.04-arm]
# No .deb host-coupling here (pure-Java uber jar + URL manifest) — free to track
# the current LTS image.
os: [ubuntu-24.04, ubuntu-24.04-arm]
env:
GRADLE_CACHE_URL: ${{ secrets.GRADLE_CACHE_URL }}
GRADLE_CACHE_USERNAME: ${{ secrets.GRADLE_CACHE_USERNAME }}

View File

@@ -12,6 +12,9 @@ on:
run_coverage:
type: boolean
default: true
run_desktop_builds:
type: boolean
default: false
run_desktop_flatpak_src:
type: boolean
default: false
@@ -93,6 +96,7 @@ jobs:
contents: read
timeout-minutes: 30
needs: setup
if: inputs.run_lint == true
env:
VERSION_CODE: ${{ needs.setup.outputs.version_code }}
@@ -112,13 +116,8 @@ jobs:
install_jetbrains_jdk: 'true'
- name: Lint, Analysis & KMP Smoke Compile
if: inputs.run_lint == true
run: ./gradlew spotlessCheck detekt androidApp:lintFdroidDebug androidApp:lintGoogleDebug core:barcode:lintFdroidDebug core:barcode:lintGoogleDebug kmpSmokeCompile -Pci=true --continue
- name: KMP Smoke Compile (lint skipped)
if: inputs.run_lint == false
run: ./gradlew kmpSmokeCompile -Pci=true --continue
# ── Screenshot Test Validation ──────────────────────────────────────
screenshot-check:
runs-on: ubuntu-24.04
@@ -156,13 +155,14 @@ jobs:
if-no-files-found: warn
# ── Reproducible Build Verification ─────────────────────────────────
# Only runs on main push and merge-queue (not PRs) to keep PR feedback fast.
# Only runs in the merge queue (not PRs, to keep PR feedback fast; not main pushes,
# which are the same merge commit the queue just verified).
rb-check:
runs-on: ubuntu-24.04
permissions:
contents: read
timeout-minutes: 30
if: inputs.run_lint == true && (github.event_name == 'push' || github.event_name == 'merge_group')
if: inputs.run_lint == true && github.event_name == 'merge_group'
steps:
- name: Checkout code

View File

@@ -8,6 +8,11 @@ on:
- 'build.gradle.kts'
- 'settings.gradle.kts'
- '.github/workflows/verify-flatpak.yml'
# The desktop module's build config shapes the uber jar the flatpak wraps.
- 'desktopApp/**'
# The offline manifest pins the Gradle distribution independently of the wrapper —
# a wrapper bump without a manifest update breaks the offline build silently.
- 'gradle/wrapper/**'
workflow_dispatch:
permissions: