Model B: DIRECT messages retry until delivered (proof-wait timeout) #274
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Tests | |
| on: | |
| pull_request: | |
| branches: [main] | |
| push: | |
| branches: [main] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| test: | |
| # macos-26 carries Xcode 26.x, which the embedded-Python (Columba) scheme | |
| # needs — the Python.framework clang module map fails to resolve under | |
| # Xcode 16.x ("module 'Python' … not defined in any loaded module map"), | |
| # which is what devs build with locally (26.x). See the Select Xcode step. | |
| runs-on: macos-26 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Clone sibling packages | |
| run: | | |
| git clone https://github.com/torlando-tech/reticulum-swift.git ../reticulum-swift | |
| git clone https://github.com/torlando-tech/LXMF-swift.git ../LXMF-swift | |
| git clone https://github.com/torlando-tech/LXST-swift.git ../LXST-swift | |
| # The Python-backend (`Columba`) scheme links Python.xcframework + the | |
| # bundled wheels; without these the build phase that copies them fails. | |
| # Fetch them before any xcodebuild step (pinned deps → reproducible). | |
| - name: Fetch Python framework + wheels | |
| run: | | |
| support/fetch-python.sh | |
| support/fetch-wheels.sh | |
| - name: Select Xcode | |
| run: | | |
| echo "Available Xcodes:"; ls -d /Applications/Xcode_*.app 2>/dev/null || true | |
| # Pick the newest Xcode on the runner (was pinned to 16.x, which can't | |
| # build the Python scheme — see runs-on comment). Version-sort so 26.x | |
| # wins over 16.x regardless of exact point release. | |
| XCODE=$(ls -d /Applications/Xcode_*.app 2>/dev/null | sort -V | tail -1) | |
| echo "Using $XCODE" | |
| sudo xcode-select -s "$XCODE" | |
| xcodebuild -version | |
| # Resolve the local Swift packages (reticulum-swift / LXMF-swift / LXST-swift) | |
| # once, up front. Without this the `xcodebuild test` action can intermittently | |
| # fail to resolve LXMFSwift/ReticulumSwift for the ColumbaAppTests target even | |
| # though the app build steps resolved them fine — a fresh-runner resolution | |
| # race. One explicit resolve makes every later build/test step reuse it. | |
| - name: Resolve Swift packages | |
| run: | | |
| # Pre-warm both schemes' package deps (same sibling packages, same | |
| # .xcodeproj, but be explicit so the Swift-native build benefits too). | |
| xcodebuild -resolvePackageDependencies \ | |
| -project Columba.xcodeproj \ | |
| -scheme Columba | |
| xcodebuild -resolvePackageDependencies \ | |
| -project Columba.xcodeproj \ | |
| -scheme Columba-Swift | |
| - name: Find simulator | |
| id: sim | |
| run: | | |
| DEVICE_ID=$(xcrun simctl list devices available -j \ | |
| | python3 -c "import sys,json; devs=json.load(sys.stdin)['devices']; print(next(d['udid'] for rt in devs for d in devs[rt] if 'iPhone' in d['name']))") | |
| echo "device_id=$DEVICE_ID" >> "$GITHUB_OUTPUT" | |
| echo "Using simulator: $DEVICE_ID" | |
| - name: Build (Python backend — default) | |
| run: | | |
| xcodebuild build \ | |
| -project Columba.xcodeproj \ | |
| -scheme Columba \ | |
| -sdk iphonesimulator \ | |
| -destination "id=${{ steps.sim.outputs.device_id }}" \ | |
| CODE_SIGN_IDENTITY=- \ | |
| CODE_SIGNING_REQUIRED=NO \ | |
| CODE_SIGNING_ALLOWED=YES \ | |
| DEVELOPMENT_TEAM="" \ | |
| PROVISIONING_PROFILE_SPECIFIER="" | |
| # Verify the native reticulum-swift/LXMF-swift backend variant also builds | |
| # (COLUMBA_BACKEND_SWIFT). The UI/AppServices are backend-agnostic, so this | |
| # guards against the Swift backend or its seam drifting out of compile. | |
| - name: Build (Swift-native backend) | |
| run: | | |
| xcodebuild build \ | |
| -project Columba.xcodeproj \ | |
| -scheme Columba-Swift \ | |
| -sdk iphonesimulator \ | |
| -destination "id=${{ steps.sim.outputs.device_id }}" \ | |
| CODE_SIGN_IDENTITY=- \ | |
| CODE_SIGNING_REQUIRED=NO \ | |
| CODE_SIGNING_ALLOWED=YES \ | |
| DEVELOPMENT_TEAM="" \ | |
| PROVISIONING_PROFILE_SPECIFIER="" | |
| - name: Build and run tests | |
| run: | | |
| xcodebuild test \ | |
| -project Columba.xcodeproj \ | |
| -scheme Columba \ | |
| -sdk iphonesimulator \ | |
| -destination "id=${{ steps.sim.outputs.device_id }}" \ | |
| -only-testing:ColumbaAppTests \ | |
| -resultBundlePath TestResults.xcresult \ | |
| -enableCodeCoverage YES \ | |
| CODE_SIGN_IDENTITY=- \ | |
| CODE_SIGNING_REQUIRED=NO \ | |
| CODE_SIGNING_ALLOWED=YES \ | |
| DEVELOPMENT_TEAM="" \ | |
| PROVISIONING_PROFILE_SPECIFIER="" | |
| - name: Export coverage report (function-level lcov) | |
| run: | | |
| set -euo pipefail | |
| # Coverage on iOS app projects with -resultBundlePath lives | |
| # inside the .xcresult bundle. The only toolchain path that | |
| # reads it cleanly under Xcode 16 is | |
| # `xccov view --report --json`, which exposes function-level | |
| # granularity (no per-line `DA:` records). Per-line data | |
| # would require either a working `xccov view --file` (broken | |
| # against both the bare .xcresult and `xcresulttool export | |
| # coverage`'s output: "Error: unrecognized file format") or | |
| # a raw .profdata (Xcode embeds it inside the xcresult, not | |
| # in DerivedData). codecov's `patch` check is configured | |
| # `informational: true` so the absence of line records is | |
| # advisory rather than blocking. | |
| XCRESULT=TestResults.xcresult | |
| if [ ! -d "$XCRESULT" ]; then | |
| echo "$XCRESULT does not exist" >&2 | |
| exit 1 | |
| fi | |
| mkdir -p coverage | |
| xcrun xccov view --report --json "$XCRESULT" > coverage/coverage.json | |
| python3 <<'PY' | |
| import json, sys | |
| data = json.load(open('coverage/coverage.json')) | |
| out = [] | |
| file_count = 0 | |
| for target in data.get('targets', []): | |
| for f in target.get('files', []): | |
| path = f['path'] | |
| # Filter out tests + third-party packages so the | |
| # report shows first-party app code only. | |
| if 'Tests' in path or 'SourcePackages' in path or 'DerivedData' in path: | |
| continue | |
| out.append(f"SF:{path}") | |
| for fn in f.get('functions', []): | |
| ln = fn.get('lineNumber', 0) | |
| hits = fn.get('executionCount', 0) | |
| name = fn.get('name', '?') | |
| out.append(f"FN:{ln},{name}") | |
| out.append(f"FNDA:{hits},{name}") | |
| out.append("end_of_record") | |
| file_count += 1 | |
| with open('coverage/coverage.lcov', 'w') as fh: | |
| fh.write("\n".join(out) + "\n") | |
| print(f"Wrote {file_count} files, {len(out)} total lcov lines") | |
| if file_count == 0: | |
| print("ERROR: zero files — coverage extraction is broken", file=sys.stderr) | |
| sys.exit(1) | |
| PY | |
| head -5 coverage/coverage.lcov | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| files: coverage/coverage.lcov | |
| fail_ci_if_error: false |