Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .flutter-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.27.4
253 changes: 168 additions & 85 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,175 +2,258 @@ name: CI

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

# Cancel in-flight runs for the same branch on new pushes
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
# ── Dart analysis + format ──────────────────────────────────────────────────
analyze:
name: Dart Analyze
name: Analyze & Format
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- name: Checkout code
uses: actions/checkout@v4
- uses: actions/checkout@v4

- name: Setup Flutter
uses: subosito/flutter-action@v2
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.19.0'
channel: 'stable'
flutter-version-file: .flutter-version
channel: stable
cache: true

- name: Get dependencies
- name: Get dependencies (plugin)
run: flutter pub get

- name: Verify formatting
run: dart format --output=none --set-exit-if-changed .
- name: Get dependencies (gen)
working-directory: native_workmanager_gen
run: dart pub get

- name: Verify formatting (plugin)
run: dart format --output=none --set-exit-if-changed lib test

- name: Verify formatting (gen)
working-directory: native_workmanager_gen
run: dart format --output=none --set-exit-if-changed lib

- name: Analyze plugin
run: flutter analyze --fatal-warnings

- name: Analyze project
run: flutter analyze --no-fatal-infos
- name: Analyze gen package
working-directory: native_workmanager_gen
run: dart analyze --fatal-warnings

- name: Pub publish dry-run (plugin)
run: dart pub publish --dry-run

- name: Pub publish dry-run (gen)
working-directory: native_workmanager_gen
run: dart pub publish --dry-run

# ── Dart unit tests ─────────────────────────────────────────────────────────
test:
name: Unit Tests
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Checkout code
uses: actions/checkout@v4
- uses: actions/checkout@v4

- name: Setup Flutter
uses: subosito/flutter-action@v2
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.19.0'
channel: 'stable'
flutter-version-file: .flutter-version
channel: stable
cache: true

- name: Get dependencies
run: flutter pub get

- name: Run tests
- name: Run plugin unit tests with coverage
run: flutter test --coverage --reporter=expanded

- name: Upload coverage to Codecov
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
files: ./coverage/lcov.info
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
verbose: true

# ── gen package tests ───────────────────────────────────────────────────────
test-gen:
name: Gen Package Tests
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4

- uses: subosito/flutter-action@v2
with:
flutter-version-file: .flutter-version
channel: stable
cache: true

- name: Get dependencies (plugin — needed by gen)
run: flutter pub get

- name: Get dependencies (gen)
working-directory: native_workmanager_gen
run: dart pub get

- name: Run gen tests
working-directory: native_workmanager_gen
run: dart test

# ── Android build + unit tests ──────────────────────────────────────────────
build-android:
name: Build Android
runs-on: ubuntu-latest
timeout-minutes: 20

timeout-minutes: 25
steps:
- name: Checkout code
uses: actions/checkout@v4
- uses: actions/checkout@v4

- name: Setup Flutter
uses: subosito/flutter-action@v2
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.19.0'
channel: 'stable'
flutter-version-file: .flutter-version
channel: stable
cache: true

- name: Setup Java 17
uses: actions/setup-java@v4
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
distribution: temurin
java-version: '17'
cache: 'gradle'
cache: gradle

- name: Get dependencies
run: flutter pub get

- name: Build Android plugin
run: |
cd example/android
./gradlew build --stacktrace
- name: Build Android (debug)
working-directory: example
run: flutter build apk --debug --no-shrink

- name: Run Android unit tests
run: |
cd example/android
./gradlew test
working-directory: example/android
run: ./gradlew testDebugUnitTest --stacktrace

# ── iOS build ───────────────────────────────────────────────────────────────
build-ios:
name: Build iOS
name: Build iOS (Simulator)
runs-on: macos-latest
timeout-minutes: 30

timeout-minutes: 40
steps:
- name: Checkout code
uses: actions/checkout@v4
- uses: actions/checkout@v4

- name: Setup Flutter
uses: subosito/flutter-action@v2
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.19.0'
channel: 'stable'
flutter-version-file: .flutter-version
channel: stable
cache: true

- name: Get dependencies
run: flutter pub get

- name: Cache CocoaPods
uses: actions/cache@v4
with:
path: example/ios/Pods
key: pods-${{ hashFiles('example/ios/Podfile.lock') }}
restore-keys: pods-

- name: Install CocoaPods
run: |
cd example/ios
pod install
working-directory: example/ios
run: pod install --repo-update

- name: Build iOS plugin
- name: Build iOS (simulator, no signing)
working-directory: example/ios
run: |
cd example/ios
xcodebuild build \
-workspace Runner.xcworkspace \
-scheme Runner \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 15,OS=latest' \
-destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' \
CODE_SIGNING_ALLOWED=NO \
| xcpretty

publish-dry-run:
name: Publish Dry Run
runs-on: ubuntu-latest
timeout-minutes: 10
if: github.event_name == 'pull_request'

# ── Integration smoke test (iOS Simulator) ──────────────────────────────────
integration-ios-simulator:
name: Integration (iOS Simulator)
runs-on: macos-latest
timeout-minutes: 40
if: github.event_name != 'pull_request'
steps:
- name: Checkout code
uses: actions/checkout@v4
- uses: actions/checkout@v4

- name: Setup Flutter
uses: subosito/flutter-action@v2
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.19.0'
channel: 'stable'
flutter-version-file: .flutter-version
channel: stable
cache: true

- name: Get dependencies
run: flutter pub get

- name: Dry run publish
run: dart pub publish --dry-run
- name: Cache CocoaPods
uses: actions/cache@v4
with:
path: example/ios/Pods
key: pods-${{ hashFiles('example/ios/Podfile.lock') }}
restore-keys: pods-

- name: Install CocoaPods
working-directory: example/ios
run: pod install

# L-04 fix: robust simulator selection with fallback chain
- name: Resolve simulator UDID
id: sim
run: |
# Try iPhone 16 first, fall back to any available iPhone simulator
UDID=$(xcrun simctl list devices available | grep 'iPhone 16 ' | head -1 | grep -Eo '\([A-F0-9-]+\)' | tr -d '()')
if [ -z "$UDID" ]; then
UDID=$(xcrun simctl list devices available | grep 'iPhone' | grep -v 'Plus\|Pro\|Max' | head -1 | grep -Eo '\([A-F0-9-]+\)' | tr -d '()')
fi
if [ -z "$UDID" ]; then
echo "❌ No iPhone simulator found. Available devices:"
xcrun simctl list devices available
exit 1
fi
echo "Using simulator UDID: $UDID"
echo "udid=$UDID" >> "$GITHUB_OUTPUT"

status-check:
- name: Boot simulator
run: xcrun simctl boot "${{ steps.sim.outputs.udid }}" || true

- name: Run integration tests (simulator subset)
working-directory: example
run: |
flutter test integration_test/device_integration_test.dart \
-d "${{ steps.sim.outputs.udid }}" \
--dart-define=SIMULATOR=true \
--timeout=120s

# ── Gate ───────────────────────────────────────────────────────────────────
all-checks:
name: All Checks Passed
runs-on: ubuntu-latest
needs: [analyze, test, build-android, build-ios]
needs: [analyze, test, test-gen, build-android, build-ios]
if: always()

steps:
- name: Check all jobs
- name: Verify all required jobs succeeded
run: |
if [[ "${{ needs.analyze.result }}" != "success" ]] || \
[[ "${{ needs.test.result }}" != "success" ]] || \
[[ "${{ needs.build-android.result }}" != "success" ]] || \
[[ "${{ needs.build-ios.result }}" != "success" ]]; then
echo "❌ One or more jobs failed"
exit 1
fi
echo "✅ All checks passed!"
results=(
"${{ needs.analyze.result }}"
"${{ needs.test.result }}"
"${{ needs.test-gen.result }}"
"${{ needs.build-android.result }}"
"${{ needs.build-ios.result }}"
)
for r in "${results[@]}"; do
if [[ "$r" != "success" ]]; then
echo "❌ One or more required jobs failed or were skipped."
exit 1
fi
done
echo "✅ All required checks passed."
Loading
Loading