Sensor CI #4
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: Sensor CI | |
| on: | |
| push: | |
| branches: [main, master] | |
| pull_request: | |
| schedule: | |
| # Every Monday at 02:00 UTC | |
| - cron: '0 2 * * 1' | |
| jobs: | |
| test: | |
| runs-on: macos-15 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Show Xcode and simulator info | |
| run: | | |
| xcodebuild -version | |
| swift --version | |
| xcrun simctl list runtimes | |
| xcrun simctl list devices available | |
| - name: Resolve package dependencies | |
| run: swift package resolve | |
| - name: Detect scheme and simulator | |
| run: | | |
| SCHEME=$(swift package dump-package | python3 -c "import json,sys; print(json.load(sys.stdin)['name'])") | |
| echo "SCHEME=$SCHEME" >> "$GITHUB_ENV" | |
| # Find the latest available iPhone simulator name (name-based, more robust than UDID) | |
| SIM_NAME=$(xcrun simctl list devices available --json | python3 -c " | |
| import json, sys | |
| devs = json.load(sys.stdin)['devices'] | |
| iphones = [ | |
| (rt, d['name']) | |
| for rt, dl in devs.items() | |
| for d in dl | |
| if 'iOS' in rt and 'iPhone' in d['name'] and d.get('isAvailable', True) | |
| ] | |
| iphones.sort(reverse=True) | |
| print(iphones[0][1] if iphones else 'iPhone 16') | |
| ") | |
| echo "SIM_NAME=$SIM_NAME" >> "$GITHUB_ENV" | |
| echo "Detected scheme: $SCHEME" | |
| echo "Detected simulator: $SIM_NAME" | |
| - name: Build | |
| id: build | |
| run: | | |
| xcodebuild build \ | |
| -scheme "$SCHEME" \ | |
| -destination "platform=iOS Simulator,name=$SIM_NAME" \ | |
| -sdk iphonesimulator \ | |
| 2>&1 | tee build_output.txt; exit "${PIPESTATUS[0]}" | |
| continue-on-error: true | |
| - name: Test | |
| id: test | |
| if: steps.build.outcome == 'success' | |
| run: | | |
| xcodebuild test \ | |
| -scheme "$SCHEME" \ | |
| -destination "platform=iOS Simulator,name=$SIM_NAME" \ | |
| -sdk iphonesimulator \ | |
| -configuration Debug \ | |
| -enableCodeCoverage YES \ | |
| 2>&1 | tee test_output.txt; exit "${PIPESTATUS[0]}" | |
| continue-on-error: true | |
| - name: Create or update issue on failure | |
| if: steps.build.outcome == 'failure' || steps.test.outcome == 'failure' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| if [ "${{ steps.build.outcome }}" = "failure" ]; then | |
| FAILED="build" | |
| LOG_FILE="build_output.txt" | |
| else | |
| FAILED="test" | |
| LOG_FILE="test_output.txt" | |
| fi | |
| ERRORS=$(grep -E 'error:|FAILED|\*\* BUILD FAILED' "$LOG_FILE" 2>/dev/null | head -60 || true) | |
| [ -z "$ERRORS" ] && ERRORS="(no error lines captured — see full logs above)" | |
| RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" | |
| SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7) | |
| TODAY=$(date -u +%Y-%m-%d) | |
| BODY="## :x: Automated CI ${FAILED} failure | |
| | | | | |
| |---|---| | |
| | **Workflow run** | [View logs](${RUN_URL}) | | |
| | **Trigger** | \`${{ github.event_name }}\` | | |
| | **Ref** | \`${{ github.ref }}\` | | |
| | **Commit** | \`${SHORT_SHA}\` | | |
| ## Error summary | |
| \`\`\` | |
| ${ERRORS} | |
| \`\`\`" | |
| # Ensure ci-failure label exists (ignore error if already present) | |
| gh label create ci-failure --color d73a4a --description "Automated CI test failure" 2>/dev/null || true | |
| # Find existing open ci-failure issue number | |
| ISSUE_NUMBER=$(gh issue list --state open --label ci-failure --json number --jq '.[0].number // empty') | |
| if [ -n "$ISSUE_NUMBER" ]; then | |
| gh issue comment "$ISSUE_NUMBER" --body "### :warning: New failure (${TODAY}) | |
| ${BODY}" | |
| else | |
| gh issue create \ | |
| --title "[CI] ${FAILED} failure detected" \ | |
| --body "$BODY" \ | |
| --label ci-failure | |
| fi | |
| - name: Close ci-failure issue on success | |
| if: steps.build.outcome == 'success' && steps.test.outcome == 'success' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" | |
| ISSUE_NUMBERS=$(gh issue list --state open --label ci-failure --json number --jq '.[].number') | |
| for ISSUE_NUMBER in $ISSUE_NUMBERS; do | |
| gh issue comment "$ISSUE_NUMBER" \ | |
| --body ":white_check_mark: All tests are passing again. See [run ${{ github.run_id }}](${RUN_URL})." | |
| gh issue close "$ISSUE_NUMBER" | |
| done | |
| - name: Fail job if build or tests failed | |
| if: steps.build.outcome == 'failure' || steps.test.outcome == 'failure' | |
| run: | | |
| echo "Build outcome: ${{ steps.build.outcome }}" | |
| echo "Test outcome: ${{ steps.test.outcome }}" | |
| exit 1 |