native macOS codings agent orchestrator prowl.onev.cat
Something went wrong. Try again.
YAML
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153name: test
# The Mac app. The mirror clients have their own workflows, and a change to only those# clients does not run this one.on: push: branches: - main paths-ignore: - Mirror/iOS/** - Mirror/Android/** - .github/workflows/mirror-*.yml pull_request: paths-ignore: - Mirror/iOS/** - Mirror/Android/** - .github/workflows/mirror-*.yml
concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true
jobs: build: # The Swift 6.4 toolchain selects different package manifests than Swift 6.3 and earlier, # so .package.resolved changes with the Xcode major version. Use the same Xcode as local # builds and releases. GitHub marks this image as a preview until a macos-27 label exists. runs-on: xcode-27 # A green run takes 6 to 10 minutes, and a slow runner has taken 2.5 times as long. The test # watchdog stops a hung test run sooner; this limit stops anything else that hangs. Without # it the default is 6 hours, so a stuck run had to be cancelled by hand. timeout-minutes: 45 permissions: contents: read env: PROWL_DERIVED_DATA_PATH: ${{ github.workspace }}/build/ci-derived-data MISE_HTTP_TIMEOUT: 120 MISE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true steps: - uses: actions/checkout@v6 with: submodules: recursive - uses: ./.github/actions/setup-macos - name: Stage debug app resources run: make embed-cli-debug embed-docs embed-skills - name: Generate workspace run: make ensure-project - name: Build app and run tests in parallel shell: bash run: | set -euo pipefail mkdir -p build/ci-logs
run_task() { local name="$1" shift local log="build/ci-logs/${name}.log" local started=$SECONDS # Capture the command's own exit code. Reading $? after an # `if cmd; then ...; fi` block yields the `if` status (0 when the # condition is false and there is no else), which silently masked # failures and let the step pass on a failing build. local status=0 # stdout is buffered into the log and printed as one folded group # when the task ends. stderr streams straight to the console with a # task prefix so progress (the `make test-app` heartbeat, `swift # test` results, make errors) shows while the task is still # running. The group is opened only around the buffered dump: a # `::group::` printed at task start would fold the streamed lines # away too, and GitHub does not nest groups from parallel tasks. # The stderr redirection comes first: redirections apply left to # right, and the process substitution inherits stdout as it is at # that moment, so placing it after `>"$log"` would send the # streamed lines into the log file instead of the console. "$@" 2> >(tee -a "build/ci-logs/${name}.stderr.log" | while IFS= read -r line; do printf '[%s] %s\n' "$name" "$line"; done) >"$log" || status=$? printf '| %s | %s | %s |\n' "$name" "$((SECONDS - started))" "$status" >"build/ci-logs/${name}.timing" echo "::group::${name} (exit ${status}, $((SECONDS - started))s)" cat "$log" echo "::endgroup::" return "$status" }
# A hung test run prints nothing until the job timeout, and a cancelled job keeps no # result bundle. After 10 minutes without test output, the watchdog samples the test # processes into build/ci-logs/hang and interrupts xcodebuild. The step then fails, # and the result bundle marks the running test as canceled. python3 scripts/ci-test-watchdog.py \ --logs 'build/test-results/*.xcresult.log' \ --results "$GITHUB_WORKSPACE/build/test-results/" \ --products "$PROWL_DERIVED_DATA_PATH/Build/Products" \ --out build/ci-logs/hang & pid_watchdog=$!
run_task app-tests make test-app & pid_app=$! # SwiftPM serializes access to .build; keep its checks in one branch. run_task cli-tests make test-cli-smoke test-cli-unit test-cli-integration & pid_cli=$! # Lint and the localization check need no build, so they run beside it. run_task checks make lint check-localization test-scripts & pid_checks=$!
failed=0 wait "$pid_app" || failed=1 wait "$pid_cli" || failed=1 wait "$pid_checks" || failed=1 kill "$pid_watchdog" 2>/dev/null || true { printf '## Build and test wall time\n\n| Task | Seconds | Exit status |\n| --- | ---: | ---: |\n' cat build/ci-logs/*.timing printf '\nTasks run in parallel; their times must not be added.\n\n' if [ -d build/ci-logs/hang ]; then printf '**The test output stopped.** The watchdog interrupted xcodebuild. The xcresult bundle names the canceled test, and ci-logs/hang in the logs artifact has the process samples.\n\n' fi python3 scripts/summarize-xcode-build.py build/test-results/*.build.json build/test-results/*.action.json } >> "$GITHUB_STEP_SUMMARY" exit "$failed" - name: Collect mirror diagnostics if: always() shell: bash run: | mkdir -p build/ci-logs /usr/bin/log show --last 30m --style compact --info --debug \ --predicate 'category == "MirrorPairing" OR process == "mDNSResponder"' \ > build/ci-logs/mirror-system.log 2>&1 || true - name: Save build input times run: | python3 scripts/ci-source-mtimes.py save python3 scripts/ci-source-mtimes.py save --scope app --manifest "$PROWL_DERIVED_DATA_PATH/source-times.json" - uses: ./.github/actions/save-macos-caches - name: Upload build and test logs if: always() uses: actions/upload-artifact@v4 with: name: build-test-logs-${{ github.run_id }}-${{ github.run_attempt }} path: | build/ci-logs/ build/test-results/*.log build/test-results/*.build.json build/test-results/*.action.json if-no-files-found: ignore retention-days: 7 - name: Upload xcresult bundle (on failure) # A job that reaches its timeout is cancelled, and its partial results are still useful. if: failure() || cancelled() uses: actions/upload-artifact@v4 with: name: xcresult-${{ github.run_id }}-${{ github.run_attempt }} path: build/test-results/**/*.xcresult if-no-files-found: ignore retention-days: 7