Skip to content

CI: per-project path-filtered workflows + per-platform releases#296

Merged
patrickrb merged 8 commits into
devfrom
feat/ci-per-project
Jun 19, 2026
Merged

CI: per-project path-filtered workflows + per-platform releases#296
patrickrb merged 8 commits into
devfrom
feat/ci-per-project

Conversation

@patrickrb

Copy link
Copy Markdown
Owner

Why

The repo holds three FT8 clients that share one C DSP core (ft8af/app/src/main/cpp/), but all CI was Android-only and fired on every push/PR with no path filtering. An iOS-only change still ran the full Android NDK build, and iOS/desktop had no CI at all. Releases were Android-only.

What changed

Split into per-project pipelines that only run when their files — or the shared C core — change:

Workflow Trigger scope Does
android.yml (renamed from build-release.yml) ft8af/** or shared core unit+coverage, instrumented, folds in static analysis, signed APK/AAB + GitHub Release + Play publish. All prior behavior preserved (NDK retry, signing, tag derivation, play-publish concurrency); job names test/instrumented/build unchanged.
ios.yml (new) ios/** or shared core swift test on FT8AFKit + unsigned iOS-Simulator build (xcodegen generatexcodebuild) on macos-14. No release (needs Apple signing).
desktop.yml (new) desktop/** or shared core Tauri compile-check on Windows/macOS/Linux for PRs; on main/desktop-v* builds native bundles on all three OSes and publishes a namespaced desktop-v* / desktop-dev-N release via tauri-action.

native-tests.yml stays always-on as the shared-core golden gate. main-gate.yml and the discord workflows are untouched. static-analysis.yml was deleted (merged into android.yml).

Behavior model

  • PR → only the affected project's checks (+ always: native golden tests, main-gate).
  • push dev → only affected platform(s) build + publish a prerelease (dev-N / desktop-dev-N).
  • push mainall platforms build, one release each (v* Android+Play, desktop-v*); iOS = verify only.
  • tag v* → Android production release (unchanged). tag desktop-v* → desktop production release.

"Affected" = project's own paths or the shared C core changed.

⚠️ Required: update branch protection after merge

Path-filtered jobs can be skipped, and a skipped required check leaves branch protection stuck "pending" (already noted in native-tests.yml). Each workflow ends with an always-run aggregator that is the intended required check. In Settings → Branches → main (and dev if protected):

  • Add required checks: android-gate, ios-gate, desktop-gate
  • Keep: FT8 encode/hash golden vectors, enforce-source-is-dev
  • Remove direct requirements on Unit tests & coverage / Build APK (now reached via android-gate)

Verification

  • actionlint clean on all workflows (locally).
  • Routing to confirm on first runs: an ios/-only change runs only iOS (android/desktop gates still green via all-skipped); a desktop/-only change runs only desktop; a ft8af/app/src/main/cpp/** change runs all three.

Notes / risks

  • Android keeps bare v* tags (drives Play + version history); only desktop is namespaced — this is the "separate release per platform" layout requested.
  • macOS/Linux desktop bundle legs may surface portability gaps (e.g. vendored Hamlib is Windows-only DLLs) on first run — that's expected feedback, not a regression introduced here.
  • iOS produces no installable until Apple cert + provisioning are added as secrets; the chosen scope (tests + simulator build) needs neither.

🤖 Generated with Claude Code

…eases

The repo holds three FT8 clients sharing one C DSP core, but all CI was
Android-only and fired on every push/PR with no path filtering — an iOS-only
change ran the full Android NDK build, and iOS/desktop had no CI at all.

Split into per-project pipelines that only run when their files (or the shared
core at ft8af/app/src/main/cpp/**) change:

- android.yml: renamed from build-release.yml, folds in static-analysis.yml as
  a job, adds a `detect` (dorny/paths-filter) gate and an always-run
  `android-gate` aggregator. All existing behavior (NDK retry, signing, tag
  derivation, GitHub Release, Play internal publish, play-publish concurrency)
  is preserved; job names test/instrumented/build are unchanged.
- ios.yml (new): swift test on FT8AFKit + unsigned iOS-Simulator build
  (xcodegen generate -> xcodebuild) on macos-14; `ios-gate` aggregator.
- desktop.yml (new): Tauri compile-check on Windows/macOS/Linux for PRs and
  non-release pushes; on main / desktop-v* tags builds native bundles on all
  three OSes and publishes a namespaced desktop-v* / desktop-dev-N release via
  tauri-action; `desktop-gate` aggregator.

Behavior model: PRs run only the affected project's checks; dev pushes build
only affected platform(s) as prereleases; main pushes / tags build everything
(one release per platform). native-tests.yml stays always-on as the shared-core
gate; main-gate.yml and the discord workflows are untouched.

Because path-filtered jobs can be skipped, the *-gate jobs always run and are
the intended required status checks (a skipped required check otherwise leaves
branch protection stuck "pending"). Branch protection must be updated to require
android-gate / ios-gate / desktop-gate instead of the inner jobs — documented in
the PR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Jun 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 11.18%. Comparing base (05394ec) to head (5fe2f4e).
⚠️ Report is 13 commits behind head on dev.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##                dev     #296      +/-   ##
============================================
+ Coverage     11.15%   11.18%   +0.02%     
  Complexity      105      105              
============================================
  Files            81       81              
  Lines         11426    11430       +4     
  Branches       2051     2052       +1     
============================================
+ Hits           1275     1278       +3     
- Misses        10023    10024       +1     
  Partials        128      128              

see 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI 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.

Pull request overview

This PR restructures CI for the repo’s multi-client layout by splitting Android/iOS/desktop into separate, path-filtered workflows (including the shared C DSP core) and introducing per-platform release behavior (Android v*, desktop desktop-v*, iOS verify-only).

Changes:

  • Replaced the monolithic Android-only CI triggering with per-project change detection and “always-run” gate jobs intended for branch protection (android-gate, ios-gate, desktop-gate).
  • Added new iOS workflow to run swift test for FT8AFKit and perform an unsigned iOS Simulator build on macOS.
  • Added new desktop workflow to compile-check on PRs and build/publish Tauri bundles on main pushes and desktop-v* tags; removed standalone static-analysis.yml by folding it into android.yml.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
.github/workflows/android.yml Adds Android path detection, folds static analysis into Android CI, and introduces android-gate as a branch-protection-friendly aggregator.
.github/workflows/ios.yml Introduces path-filtered iOS verification (Swift package tests + simulator build) with an always-run ios-gate.
.github/workflows/desktop.yml Introduces path-filtered desktop CI and release publishing via Tauri across Windows/macOS/Linux with an always-run desktop-gate.
.github/workflows/static-analysis.yml Deleted in favor of embedding lint/detekt/ktlint into android.yml.
Comments suppressed due to low confidence (1)

.github/workflows/android.yml:585

  • android-gate doesn't depend on detect, so if detect fails, test/build can be skipped and the gate still reports success. Since android-gate is intended to be the required check, it should fail on a failed/cancelled detect as well.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/ios.yml Outdated
Comment thread .github/workflows/desktop.yml Outdated
patrickrb and others added 7 commits June 18, 2026 14:25
…uild

Address Copilot review on PR #296:
- android-gate / ios-gate / desktop-gate now depend on (and check) their
  detect/version prerequisites, not just the build/verify/test legs. A
  failed `detect` (or desktop `version`) skips the downstream jobs, and a
  skip alone was being read as "path-filtered, nothing to do" — letting a
  broken workflow report success on the required check. Each gate now fails
  if any prerequisite result is failure/cancelled; a genuine path-skip
  (detect succeeded, run=false) still passes.

Also fix the iOS verify job, which failed to compile: ft8_lib's `fmtmsg`
collided with the POSIX `fmtmsg(long, ...)` from <fmtmsg.h>, visible when
the shared C core is built under the macOS/Apple SDK (FT8AFKit swift test).
Rename the unused helper to ft8_fmtmsg (no call sites anywhere in the repo).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CDu3dtuN89JsRqxFsq1ow9
…roject

The ios.yml simulator-build leg failed with "future Xcode project file
format (77)": XcodeGen 2.45.4 defaults the generated pbxproj to objectVersion
77 (Xcode 16-only), but the macos-14 runner's Xcode 15.4 — matching the
project's pinned xcodeVersion 15.0 — can't read it. Pin objectVersion 56,
which Xcode 15 opens and Xcode 16 still accepts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CDu3dtuN89JsRqxFsq1ow9
XcodeGen 2.45.x always emits the pbxproj in Xcode 16's object format
(objectVersion 77); Xcode 15.4 on the macos-14 runner can't open it
("future Xcode project file format (77)"). Pinning objectVersion via
project.yml had no effect (not an honored XcodeGen option), so build the
simulator leg on macos-15 / Xcode 16 instead. The iOS 17 deployment target
still builds under the newer toolchain.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CDu3dtuN89JsRqxFsq1ow9
The ubuntu desktop build failed in libudev-sys's build script:
"Package libudev was not found in the pkg-config search path." The
serialport crate (CAT rig control) needs libudev on Linux, which the apt
step wasn't installing. Add libudev-dev alongside the existing Tauri/GTK
system deps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CDu3dtuN89JsRqxFsq1ow9
With libudev-dev in place the Linux build now gets past serialport and
fails next in alsa-sys: "Package alsa was not found in the pkg-config
search path." The audio crate (cpal/rodio) needs ALSA on Linux. Add
libasound2-dev to the apt step.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CDu3dtuN89JsRqxFsq1ow9
The Linux desktop build hung indefinitely on "Install Linux dependencies"
while the macOS/Windows builds finished in minutes. Two fixes:

- ubuntu-latest is now Ubuntu 24.04, which dropped libappindicator3-dev.
  Switch to libayatana-appindicator3-dev (Tauri's current Linux prereq).
- Harden apt against the classic CI hangs: DEBIAN_FRONTEND=noninteractive
  (suppress debconf prompts -y misses) and -o DPkg::Lock::Timeout=600 (wait
  up to 10 min for the dpkg lock instead of forever, then fail loudly).
  Also --no-install-recommends to keep the install lean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@patrickrb patrickrb merged commit ec8b5c0 into dev Jun 19, 2026
18 checks passed
@patrickrb patrickrb deleted the feat/ci-per-project branch June 19, 2026 15:22
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.

2 participants