brew install xcodegen resolves to a formula requiring Xcode 15.3, which cannot be installed on the macOS 12 runner (Xcode 14.2), failing run 31390 before project generation. Install a pinned prebuilt XcodeGen 2.38.0 release via scripts/ensure-host-tools.sh, shared by the build-and-test and package jobs (package-release.sh also runs xcodegen). Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
182 lines
6.4 KiB
YAML
182 lines
6.4 KiB
YAML
name: macOS CI
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- develop
|
|
tags:
|
|
- 'v*'
|
|
pull_request:
|
|
branches:
|
|
- develop
|
|
|
|
jobs:
|
|
build-and-test:
|
|
# Prefer a self-hosted Mac runner if your Gitea has one. If not,
|
|
# macos-14 works for this pipeline.
|
|
runs-on: macos-12
|
|
env:
|
|
DERIVED: build/DerivedData-test
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Assert Xcode 14 toolchain
|
|
run: xcodebuild -version | grep -E "Xcode 14." || (echo "Unexpected Xcode version" && exit 1)
|
|
|
|
# Homebrew's xcodegen formula requires Xcode 15.3, which cannot be
|
|
# installed on macOS 12 (#109). The script installs a pinned
|
|
# prebuilt release instead.
|
|
- name: Ensure host tools
|
|
run: scripts/ensure-host-tools.sh
|
|
|
|
- name: Generate Xcode project
|
|
run: xcodegen generate --spec project.yml
|
|
|
|
- name: Build for testing (universal)
|
|
run: |
|
|
xcodebuild build-for-testing \
|
|
-scheme ICCery \
|
|
-destination 'platform=macOS' \
|
|
-derivedDataPath "$DERIVED" \
|
|
-configuration Debug \
|
|
ARCHS='arm64 x86_64' \
|
|
ONLY_ACTIVE_ARCH=NO \
|
|
CODE_SIGNING_ALLOWED=YES \
|
|
CODE_SIGN_IDENTITY='-'
|
|
|
|
- name: Test unit (ICCeryCoreTests)
|
|
run: |
|
|
XCTESTRUN="$(find "$DERIVED" -name 'ICCery*.xctestrun' | head -n 1)"
|
|
if [ -z "$XCTESTRUN" ] || [ ! -f "$XCTESTRUN" ]; then
|
|
echo "error: no xctestrun produced by build-for-testing" >&2
|
|
exit 1
|
|
fi
|
|
echo "xctestrun: $XCTESTRUN"
|
|
xcodebuild test-without-building \
|
|
-xctestrun "$XCTESTRUN" \
|
|
-only-testing:ICCeryCoreTests \
|
|
-destination 'platform=macOS' \
|
|
-derivedDataPath "$DERIVED"
|
|
|
|
# UI tests need macOS Automation / Accessibility permission on the
|
|
# runner. The self-hosted Mac intermittently times out enabling
|
|
# that mode (run 29700) or launches the app into
|
|
# `.runningBackground` without ever activating it (run 29804).
|
|
# Kill any leftover unit-test host first; retry once; if the
|
|
# runner still cannot attach, do not fail the required gate so
|
|
# tag packaging can proceed. Real XCTest assertion failures
|
|
# still fail the job.
|
|
- name: Test UI (ICCeryUITests)
|
|
run: |
|
|
set -o pipefail
|
|
XCTESTRUN="$(find "$DERIVED" -name 'ICCery*.xctestrun' | head -n 1)"
|
|
LOG="$DERIVED/ui-test.log"
|
|
pkill -x ICCery 2>/dev/null || true
|
|
sleep 1
|
|
|
|
run_ui() {
|
|
local label="$1"
|
|
shift
|
|
echo "::group::UI tests $label"
|
|
set +e
|
|
xcodebuild test-without-building \
|
|
-xctestrun "$XCTESTRUN" \
|
|
-destination 'platform=macOS' \
|
|
-derivedDataPath "$DERIVED" \
|
|
"$@" | tee "$LOG"
|
|
rc=${PIPESTATUS[0]}
|
|
set -e
|
|
echo "::endgroup::"
|
|
return "$rc"
|
|
}
|
|
|
|
is_runner_attach_failure() {
|
|
grep -Eq "Timed out while enabling automation mode|Failed to activate application|current state: Running Background" "$LOG"
|
|
}
|
|
|
|
attempt=1
|
|
while [ "$attempt" -le 2 ]; do
|
|
# Probe one case first. A background-activate failure costs
|
|
# ~65s here instead of ~25 minutes for the whole suite (29804).
|
|
if ! run_ui "probe attempt $attempt" \
|
|
-only-testing:ICCeryUITests/AboutHelpUITests/testAboutDialogShowsVersionAndBuildDate; then
|
|
if is_runner_attach_failure; then
|
|
echo "warning: UI runner could not attach/activate the app (attempt $attempt)"
|
|
pkill -x ICCery 2>/dev/null || true
|
|
attempt=$((attempt + 1))
|
|
sleep 8
|
|
continue
|
|
fi
|
|
echo "error: UI probe failed with a real test error" >&2
|
|
exit 1
|
|
fi
|
|
if run_ui "full suite attempt $attempt" -only-testing:ICCeryUITests; then
|
|
exit 0
|
|
fi
|
|
if is_runner_attach_failure; then
|
|
echo "warning: UI runner lost activation mid-suite (attempt $attempt)"
|
|
pkill -x ICCery 2>/dev/null || true
|
|
attempt=$((attempt + 1))
|
|
sleep 8
|
|
continue
|
|
fi
|
|
echo "error: UI tests failed with a real test error" >&2
|
|
exit 1
|
|
done
|
|
echo "warning: skipping UI tests after repeated runner attach/activate failures"
|
|
exit 0
|
|
|
|
package:
|
|
needs: build-and-test
|
|
runs-on: macos-12
|
|
if: github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/tags/v')
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
# scripts/package-release.sh runs `xcodegen generate` and dmgbuild;
|
|
# see build-and-test for why brew is not used on macOS 12 (#109).
|
|
- name: Ensure host tools
|
|
run: scripts/ensure-host-tools.sh
|
|
|
|
- name: Package release
|
|
run: scripts/package-release.sh
|
|
env:
|
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
CODESIGN_IDENTITY: ${{ secrets.CODESIGN_IDENTITY }}
|
|
DEVELOPMENT_TEAM: ${{ secrets.DEVELOPMENT_TEAM }}
|
|
NOTARIZE_APPLE_ID: ${{ secrets.NOTARIZE_APPLE_ID }}
|
|
NOTARIZE_PASSWORD: ${{ secrets.NOTARIZE_PASSWORD }}
|
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
|
|
|
- name: Prepare Node CA bundle
|
|
run: |
|
|
NODE_CA_FILE="/tmp/macos-ca-bundle.pem"
|
|
security find-certificate -a -p \
|
|
/System/Library/Keychains/SystemRootCertificates.keychain \
|
|
/Library/Keychains/System.keychain \
|
|
> "$NODE_CA_FILE" 2>/dev/null || true
|
|
if [ ! -s "$NODE_CA_FILE" ] && [ -f /etc/ssl/cert.pem ]; then
|
|
cp /etc/ssl/cert.pem "$NODE_CA_FILE"
|
|
fi
|
|
|
|
- name: Upload DMG artifact
|
|
uses: actions/upload-artifact@v3
|
|
env:
|
|
NODE_EXTRA_CA_CERTS: /tmp/macos-ca-bundle.pem
|
|
with:
|
|
name: iccery-dmg
|
|
path: ICCery-*.dmg
|
|
|
|
- name: Attach DMG to Gitea release
|
|
if: startsWith(github.ref, 'refs/tags/v')
|
|
run: scripts/attach-release-asset.sh
|
|
env:
|
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
GITHUB_TOKEN: ${{ github.token }}
|
|
GITEA_SERVER_URL: ${{ github.server_url }}
|
|
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
GITHUB_REF_NAME: ${{ github.ref_name }}
|
|
GITHUB_SHA: ${{ github.sha }}
|