Section 12/161 menit

12. Integrasi CI: Xcode Cloud dan GitHub Actions

12. Integrasi CI: Xcode Cloud dan GitHub Actions

JUnit XML Output untuk CI

swift
# Jalankan test dan hasilkan JUnit XML untuk CI parsing
swift test --enable-code-coverage \
           --result-bundle-path TestResults.xcresult \
           2>&1 | tee test_output.txt

# Atau dengan xcodebuild
xcodebuild test \
  -project MyApp.xcodeproj \
  -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' \
  -resultBundlePath TestResults.xcresult \
  CODE_SIGN_IDENTITY="-"

GitHub Actions Workflow

swift
# .github/workflows/test.yml
name: Swift Tests

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: macos-15
    steps:
      - uses: actions/checkout@v4

      - name: Select Xcode
        run: sudo xcode-select -switch /Applications/Xcode_16.app

      - name: Build and Test
        run: |
          xcodebuild test \
            -project MyApp.xcodeproj \
            -scheme MyApp \
            -destination 'platform=iOS Simulator,name=iPhone 16,OS=18.0' \
            -resultBundlePath TestResults.xcresult \
            CODE_SIGN_IDENTITY="-" \
            | xcpretty --report junit --output test-results.xml

      - name: Upload Test Results
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-results
          path: |
            TestResults.xcresult
            test-results.xml

      - name: Report Test Results
        uses: dorny/test-reporter@v1
        if: always()
        with:
          name: Swift Tests
          path: test-results.xml
          reporter: java-junit

Xcode Cloud ci_scripts

swift
#!/bin/zsh
# ci_scripts/ci_post_xcodebuild.sh

# Jalankan analisis setelah test
if [ "$CI_XCODEBUILD_ACTION" = "test" ]; then
    echo "Menganalisis test results..."

    # Cek code coverage (minimal 80%)
    COVERAGE=$(xcrun xccov view --report --json $CI_RESULT_BUNDLE_PATH \
        | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['lineCoverage'])")

    THRESHOLD=0.80
    if (( $(echo "$COVERAGE < $THRESHOLD" | bc -l) )); then
        echo "ERROR: Code coverage $COVERAGE < $THRESHOLD"
        exit 1
    fi

    echo "Code coverage: $COVERAGE ✓"
fi

Tag-Based Test Filtering di CI

swift
// Tandai test dengan tag
extension Tag {
    @Tag static var unit: Self
    @Tag static var integration: Self
    @Tag static var slow: Self
    @Tag static var networkRequired: Self
}

@Test(.tags(.unit))
func testCalculation() { /* ... */ }

@Test(.tags(.integration, .networkRequired))
func testAPIIntegration() async throws { /* ... */ }

@Test(.tags(.slow))
func testHeavyMigration() async throws { /* ... */ }
swift
# Di CI: jalankan hanya unit test (cepat)
xcodebuild test \
  -scheme MyApp \
  -destination '...' \
  -only-testing:"MyAppTests/unit"  # Filter berdasarkan tag
  # Atau gunakan -skip-testing untuk exclude

# Nightly run: semua test termasuk slow dan integration
xcodebuild test \
  -scheme MyApp \
  -destination '...'  # Tanpa filter — semua test