diff --git a/.github/workflows/size-analysis.yml b/.github/workflows/size-analysis.yml new file mode 100644 index 0000000000..c5de654d27 --- /dev/null +++ b/.github/workflows/size-analysis.yml @@ -0,0 +1,212 @@ +name: Size Analysis + +on: + push: + branches: + - main + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +env: + SENTRY_ORG: sentry-sdks + SENTRY_PROJECT: sentry-flutter + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + +jobs: + android: + name: Android + runs-on: ubuntu-latest + timeout-minutes: 30 + defaults: + run: + working-directory: packages/flutter/example + + steps: + - uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - uses: actions/setup-java@v5 + with: + distribution: 'adopt' + java-version: '17' + + - name: Read configured Flutter SDK version + id: conf + run: | + version=$(grep "version" ../../../metrics/flutter.properties | cut -d'=' -f2 | xargs) + echo "flutter=$version" >> "$GITHUB_OUTPUT" + + - name: Install Flutter v${{ steps.conf.outputs.flutter }} + uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # pin@v2.21.0 + with: + flutter-version: ${{ steps.conf.outputs.flutter }} + + - name: Install Sentry CLI + if: env.SENTRY_AUTH_TOKEN != '' + run: curl -sL https://sentry.io/get-cli/ | SENTRY_CLI_VERSION=2.58.2 sh + + - name: Determine build version + run: | + version_line=$(grep '^version:' pubspec.yaml | awk '{print $2}') + # Remove any + suffix first (for pubspec.yaml versions like 1.2.3+4) + version=${version_line%%+*} + + # Parse version: x.y.z-suffix.n → build_name=x.y.z, build_number=n + # Supports: x.y.z, x.y.z-alpha.n, x.y.z-beta.n, x.y.z-rc.n, etc. + # Note: build_name is x.y.z only (plist/Android compatibility) + if [[ $version =~ ^([0-9]+\.[0-9]+\.[0-9]+)-[a-zA-Z]+\.([0-9]+)$ ]]; then + build_name="${BASH_REMATCH[1]}" + build_number="${BASH_REMATCH[2]}" + elif [[ $version =~ ^([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then + build_name="${BASH_REMATCH[1]}" + build_number=1 + else + build_name="$version" + build_number=1 + fi + + echo "SIZE_VERSION=$build_name" >> "$GITHUB_ENV" + echo "SIZE_BUILD=$build_number" >> "$GITHUB_ENV" + + - name: Get merge base SHA + id: merge-base + if: env.SENTRY_AUTH_TOKEN != '' + run: | + if [ "${{ github.event_name }}" == "pull_request" ]; then + BASE_SHA="${{ github.event.pull_request.base.sha }}" + else + BASE_SHA=$(git merge-base HEAD origin/main || echo "${{ github.event.before }}") + fi + echo "sha=$BASE_SHA" >> "$GITHUB_OUTPUT" + + - name: Build Android Appbundle + run: | + flutter pub get + flutter build appbundle --release \ + --build-name "$SIZE_VERSION" \ + --build-number "$SIZE_BUILD" + + - name: Upload Android Bundle to Sentry Size Analysis + if: env.SENTRY_AUTH_TOKEN != '' + run: | + UPLOAD_ARGS=( + "build/app/outputs/bundle/release/app-release.aab" + "--org" "${{ env.SENTRY_ORG }}" + "--project" "${{ env.SENTRY_PROJECT }}" + "--build-configuration" "Release" + "--head-sha" "${{ github.event.pull_request.head.sha || github.sha }}" + "--head-repo-name" "${{ github.repository }}" + "--head-ref" "${{ github.head_ref || github.ref_name }}" + "--base-ref" "${{ github.base_ref || 'main' }}" + ) + + if [ -n "${{ steps.merge-base.outputs.sha }}" ]; then + UPLOAD_ARGS+=("--base-sha" "${{ steps.merge-base.outputs.sha }}") + fi + + if [ -n "${{ github.event.pull_request.number }}" ]; then + UPLOAD_ARGS+=("--pr-number" "${{ github.event.pull_request.number }}") + fi + + sentry-cli build upload "${UPLOAD_ARGS[@]}" + + ios: + name: iOS + runs-on: macos-15 + timeout-minutes: 45 + defaults: + run: + working-directory: packages/flutter/example + + steps: + - uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Read configured Flutter SDK version + id: conf + run: | + version=$(grep "version" ../../../metrics/flutter.properties | cut -d'=' -f2 | xargs) + echo "flutter=$version" >> "$GITHUB_OUTPUT" + + - name: Install Flutter v${{ steps.conf.outputs.flutter }} + uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # pin@v2.21.0 + with: + flutter-version: ${{ steps.conf.outputs.flutter }} + + - name: Install Sentry CLI + if: env.SENTRY_AUTH_TOKEN != '' + run: curl -sL https://sentry.io/get-cli/ | SENTRY_CLI_VERSION=2.58.2 sh + + - name: Determine build version + run: | + version_line=$(grep '^version:' pubspec.yaml | awk '{print $2}') + # Remove any + suffix first (for pubspec.yaml versions like 1.2.3+4) + version=${version_line%%+*} + + # Parse version: x.y.z-suffix.n → build_name=x.y.z, build_number=n + # Supports: x.y.z, x.y.z-alpha.n, x.y.z-beta.n, x.y.z-rc.n, etc. + # Note: build_name is x.y.z only (plist/Android compatibility) + if [[ $version =~ ^([0-9]+\.[0-9]+\.[0-9]+)-[a-zA-Z]+\.([0-9]+)$ ]]; then + build_name="${BASH_REMATCH[1]}" + build_number="${BASH_REMATCH[2]}" + elif [[ $version =~ ^([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then + build_name="${BASH_REMATCH[1]}" + build_number=1 + else + build_name="$version" + build_number=1 + fi + + echo "SIZE_VERSION=$build_name" >> "$GITHUB_ENV" + echo "SIZE_BUILD=$build_number" >> "$GITHUB_ENV" + + # QuickFix for failing iOS 18.0 builds https://github.com/actions/runner-images/issues/12758#issuecomment-3187115656 + - name: Switch to Xcode 16.4 for iOS 18.5 + run: sudo xcode-select --switch /Applications/Xcode_16.4.app + + - name: Build Flutter iOS + run: | + flutter pub get + flutter build ipa --release --no-codesign \ + --build-name "$SIZE_VERSION" \ + --build-number "$SIZE_BUILD" + + - name: Get merge base SHA + id: merge-base + if: env.SENTRY_AUTH_TOKEN != '' + run: | + if [ "${{ github.event_name }}" == "pull_request" ]; then + BASE_SHA="${{ github.event.pull_request.base.sha }}" + else + BASE_SHA=$(git merge-base HEAD origin/main || echo "${{ github.event.before }}") + fi + echo "sha=$BASE_SHA" >> "$GITHUB_OUTPUT" + + - name: Upload iOS XCArchive to Sentry Size Analysis + if: env.SENTRY_AUTH_TOKEN != '' + run: | + UPLOAD_ARGS=( + "build/ios/archive/Runner.xcarchive" + "--org" "${{ env.SENTRY_ORG }}" + "--project" "${{ env.SENTRY_PROJECT }}" + "--build-configuration" "Release" + "--head-sha" "${{ github.event.pull_request.head.sha || github.sha }}" + "--head-repo-name" "${{ github.repository }}" + "--head-ref" "${{ github.head_ref || github.ref_name }}" + "--base-ref" "${{ github.base_ref || 'main' }}" + ) + + if [ -n "${{ steps.merge-base.outputs.sha }}" ]; then + UPLOAD_ARGS+=("--base-sha" "${{ steps.merge-base.outputs.sha }}") + fi + + if [ -n "${{ github.event.pull_request.number }}" ]; then + UPLOAD_ARGS+=("--pr-number" "${{ github.event.pull_request.number }}") + fi + + sentry-cli build upload "${UPLOAD_ARGS[@]}" diff --git a/packages/flutter/example/android/app/build.gradle b/packages/flutter/example/android/app/build.gradle index 5f9c0798ee..56d97c4b13 100644 --- a/packages/flutter/example/android/app/build.gradle +++ b/packages/flutter/example/android/app/build.gradle @@ -27,7 +27,7 @@ if (flutterVersionName == null) { } android { - namespace = "io.sentry.samples.flutter" + namespace = "io.sentry.flutter.sample" compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 @@ -44,7 +44,7 @@ android { } defaultConfig { - applicationId "io.sentry.samples.flutter" + applicationId "io.sentry.flutter.sample" minSdkVersion flutter.minSdkVersion targetSdkVersion 35 versionCode flutterVersionCode.toInteger() diff --git a/packages/flutter/example/android/app/proguard-rules.pro b/packages/flutter/example/android/app/proguard-rules.pro index 446433a465..fb7b68f4d3 100644 --- a/packages/flutter/example/android/app/proguard-rules.pro +++ b/packages/flutter/example/android/app/proguard-rules.pro @@ -1 +1 @@ --keep class io.sentry.samples.flutter.** { *; } +-keep class io.sentry.flutter.sample.** { *; } diff --git a/packages/flutter/example/android/app/src/main/AndroidManifest.xml b/packages/flutter/example/android/app/src/main/AndroidManifest.xml index 800fb96d4c..be05427d0a 100644 --- a/packages/flutter/example/android/app/src/main/AndroidManifest.xml +++ b/packages/flutter/example/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="io.sentry.flutter.sample"> diff --git a/packages/flutter/example/android/app/src/main/kotlin/io/sentry/samples/flutter/MainActivity.kt b/packages/flutter/example/android/app/src/main/kotlin/io/sentry/samples/flutter/MainActivity.kt index 8338d96048..4386fca56b 100644 --- a/packages/flutter/example/android/app/src/main/kotlin/io/sentry/samples/flutter/MainActivity.kt +++ b/packages/flutter/example/android/app/src/main/kotlin/io/sentry/samples/flutter/MainActivity.kt @@ -1,4 +1,4 @@ -package io.sentry.samples.flutter +package io.sentry.flutter.sample import android.os.Handler import io.flutter.embedding.android.FlutterActivity diff --git a/packages/flutter/example/integration_test/integration_test.dart b/packages/flutter/example/integration_test/integration_test.dart index 0e0ecc058a..b89e6751c7 100644 --- a/packages/flutter/example/integration_test/integration_test.dart +++ b/packages/flutter/example/integration_test/integration_test.dart @@ -362,9 +362,7 @@ void main() { final contexts = await SentryFlutter.native?.loadContexts(); final appPackageInfo = await PackageInfo.fromPlatform(); - final expectedAppId = Platform.isAndroid - ? 'io.sentry.samples.flutter' - : 'io.sentry.flutter.sample'; + const expectedAppId = 'io.sentry.flutter.sample'; final expectedSdkName = Platform.isAndroid ? 'maven:sentry-android' : 'cocoapods:sentry-cocoa'; final expectedVersion = appPackageInfo.version; diff --git a/packages/flutter/test/android_platform_exception_event_processor_test.dart b/packages/flutter/test/android_platform_exception_event_processor_test.dart index 477a384ad9..e8692469e8 100644 --- a/packages/flutter/test/android_platform_exception_event_processor_test.dart +++ b/packages/flutter/test/android_platform_exception_event_processor_test.dart @@ -243,9 +243,9 @@ const _jvmStackTrace = at io.flutter.plugin.common.StandardMessageCodec.writeValue(StandardMessageCodec.java:292) at io.flutter.plugin.common.StandardMethodCodec.encodeSuccessEnvelope(StandardMethodCodec.java:59) at io.flutter.plugin.common.MethodChannel\$IncomingMethodCallHandler\$1.success(MethodChannel.java:267) - at io.sentry.samples.flutter.MainActivity.configureFlutterEngine\$lambda-0(MainActivity.kt:40) - at io.sentry.samples.flutter.MainActivity.lambda\$TiSaAm1LIEmKLVswI4BlR_5sw5Y(Unknown Source:0) - at io.sentry.samples.flutter.-\$\$Lambda\$MainActivity\$TiSaAm1LIEmKLVswI4BlR_5sw5Y.onMethodCall(Unknown Source:2) + at io.sentry.flutter.sample.MainActivity.configureFlutterEngine\$lambda-0(MainActivity.kt:40) + at io.sentry.flutter.sample.MainActivity.lambda\$TiSaAm1LIEmKLVswI4BlR_5sw5Y(Unknown Source:0) + at io.sentry.flutter.sample.-\$\$Lambda\$MainActivity\$TiSaAm1LIEmKLVswI4BlR_5sw5Y.onMethodCall(Unknown Source:2) at io.flutter.plugin.common.MethodChannel\$IncomingMethodCallHandler.onMessage(MethodChannel.java:262) at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:296) at io.flutter.embedding.engine.dart.DartMessenger.lambda\$dispatchMessageToQueue\$0\$DartMessenger(DartMessenger.java:320) diff --git a/packages/flutter/test/jvm/jvm_exception_test.dart b/packages/flutter/test/jvm/jvm_exception_test.dart index 9f3f0d2ef9..4723e12b65 100644 --- a/packages/flutter/test/jvm/jvm_exception_test.dart +++ b/packages/flutter/test/jvm/jvm_exception_test.dart @@ -208,9 +208,9 @@ java.lang.IllegalArgumentException: Unsupported value: '[Ljava.lang.StackTraceEl at io.flutter.plugin.common.StandardMessageCodec.writeValue(StandardMessageCodec.java:292) at io.flutter.plugin.common.StandardMethodCodec.encodeSuccessEnvelope(StandardMethodCodec.java:59) at io.flutter.plugin.common.MethodChannel\$IncomingMethodCallHandler\$1.success(MethodChannel.java:267) - at io.sentry.samples.flutter.MainActivity.configureFlutterEngine\$lambda-0(MainActivity.kt:40) - at io.sentry.samples.flutter.MainActivity.lambda\$TiSaAm1LIEmKLVswI4BlR_5sw5Y(Unknown Source:0) - at io.sentry.samples.flutter.-\$\$Lambda\$MainActivity\$TiSaAm1LIEmKLVswI4BlR_5sw5Y.onMethodCall(Unknown Source:2) + at io.sentry.flutter.sample.MainActivity.configureFlutterEngine\$lambda-0(MainActivity.kt:40) + at io.sentry.flutter.sample.MainActivity.lambda\$TiSaAm1LIEmKLVswI4BlR_5sw5Y(Unknown Source:0) + at io.sentry.flutter.sample.-\$\$Lambda\$MainActivity\$TiSaAm1LIEmKLVswI4BlR_5sw5Y.onMethodCall(Unknown Source:2) at io.flutter.plugin.common.MethodChannel\$IncomingMethodCallHandler.onMessage(MethodChannel.java:262) at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:296) at io.flutter.embedding.engine.dart.DartMessenger.lambda\$dispatchMessageToQueue\$0\$DartMessenger(DartMessenger.java:320) @@ -248,7 +248,7 @@ android.content.res.Resources\$NotFoundException: Unable to find resource ID #0x const platformExceptionWithEmptyStackFrames = ''' java.lang.RuntimeException: Catch this platform exception! - at io.sentry.samples.flutter.MainActivity\$configureFlutterEngine\$1.onMethodCall(MainActivity.kt:40) + at io.sentry.flutter.sample.MainActivity\$configureFlutterEngine\$1.onMethodCall(MainActivity.kt:40) at io.flutter.plugin.common.MethodChannel\$IncomingMethodCallHandler.onMessage(MethodChannel.java:258) at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295) at io.flutter.embedding.engine.dart.DartMessenger.lambda\$dispatchMessageToQueue\$0\$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:322) diff --git a/packages/flutter/test/jvm/jvm_frame_test.dart b/packages/flutter/test/jvm/jvm_frame_test.dart index ca6a056aa2..56e717a1fd 100644 --- a/packages/flutter/test/jvm/jvm_frame_test.dart +++ b/packages/flutter/test/jvm/jvm_frame_test.dart @@ -68,14 +68,14 @@ void main() { test('parses frame with lambda and unknown source', () { final frame = JvmFrame.parse( - 'at io.sentry.samples.flutter.-\$\$Lambda\$MainActivity\$TiSaAm1LIEmKLVswI4BlR_5sw5Y.onMethodCall(Unknown Source:2)', + 'at io.sentry.flutter.sample.-\$\$Lambda\$MainActivity\$TiSaAm1LIEmKLVswI4BlR_5sw5Y.onMethodCall(Unknown Source:2)', ); expect(frame.fileName, 'Unknown Source'); expect(frame.lineNumber, 2); expect(frame.method, 'onMethodCall'); expect( frame.className, - 'io.sentry.samples.flutter.-\$\$Lambda\$MainActivity\$TiSaAm1LIEmKLVswI4BlR_5sw5Y', + 'io.sentry.flutter.sample.-\$\$Lambda\$MainActivity\$TiSaAm1LIEmKLVswI4BlR_5sw5Y', ); expect(frame.skippedFrames, null); expect(frame.isNativeMethod, false);