diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d550f6a14d..a7f93ccc7f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @marandaneto @krystofwoldrich @stefanosiano +* @krystofwoldrich @stefanosiano @buenaflor diff --git a/.github/workflows/flutter.yml b/.github/workflows/flutter.yml index 08af1b8336..e5c682efa9 100644 --- a/.github/workflows/flutter.yml +++ b/.github/workflows/flutter.yml @@ -160,7 +160,7 @@ jobs: steps: - uses: actions/checkout@v3 # https://github.com/CocoaPods/CocoaPods/issues/5275#issuecomment-315461879 - - run: pod lib lint ios/sentry_flutter.podspec --configuration=Debug --skip-import-validation --allow-warnings + - run: pod lib lint ios/sentry_flutter.podspec --configuration=Debug --skip-import-validation --allow-warnings --verbose swift-lint: runs-on: ubuntu-latest diff --git a/.github/workflows/flutter_integration_test.yml b/.github/workflows/flutter_integration_test.yml index 761d2771e5..9a78828164 100644 --- a/.github/workflows/flutter_integration_test.yml +++ b/.github/workflows/flutter_integration_test.yml @@ -1,12 +1,14 @@ name: flutter integration tests on: - push: - branches: - - main - - release/** - pull_request: - paths-ignore: - - 'file/**' + # Currently broken, enable after fixing + workflow_dispatch + # push: + # branches: + # - main + # - release/** + # pull_request: + # paths-ignore: + # - 'file/**' jobs: cancel-previous-workflow: @@ -16,35 +18,35 @@ jobs: uses: styfle/cancel-workflow-action@b173b6ec0100793626c2d9e6b90435061f4fc3e5 # pin@0.11.0 with: access_token: ${{ github.token }} - + test-android: runs-on: macos-latest timeout-minutes: 30 defaults: - run: - working-directory: ./flutter/example + run: + working-directory: ./flutter/example strategy: - fail-fast: false - matrix: - sdk: ['stable', 'beta'] + fail-fast: false + matrix: + sdk: ["stable", "beta"] steps: - - name: checkout - uses: actions/checkout@v3 + - name: checkout + uses: actions/checkout@v3 - - uses: actions/setup-java@v3 - with: - distribution: 'adopt' - java-version: '11' + - uses: actions/setup-java@v3 + with: + distribution: "adopt" + java-version: "11" - - uses: subosito/flutter-action@48cafc24713cca54bbe03cdc3a423187d413aafa # pin@v2.10.0 - with: - channel: ${{ matrix.sdk }} + - uses: subosito/flutter-action@48cafc24713cca54bbe03cdc3a423187d413aafa # pin@v2.10.0 + with: + channel: ${{ matrix.sdk }} - - name: flutter upgrade - run: flutter upgrade + - name: flutter upgrade + run: flutter upgrade - - name: flutter pub get - run: flutter pub get + - name: flutter pub get + run: flutter pub get - name: Gradle cache uses: gradle/gradle-build-action@v2 @@ -58,18 +60,18 @@ jobs: ~/.android/adb* key: avd-21 - - name: create AVD and generate snapshot for caching - if: steps.avd-cache.outputs.cache-hit != 'true' - uses: reactivecircus/android-emulator-runner@d94c3fbe4fe6a29e4a5ba47c12fb47677c73656b #pin@v2.28.0 - with: - working-directory: ./flutter/example - api-level: 21 - force-avd-creation: false - emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - disable-animations: false - arch: x86_64 - profile: Nexus 6 - script: echo "Generated AVD snapshot for caching." + - name: create AVD and generate snapshot for caching + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@d94c3fbe4fe6a29e4a5ba47c12fb47677c73656b #pin@v2.28.0 + with: + working-directory: ./flutter/example + api-level: 21 + force-avd-creation: false + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + disable-animations: false + arch: x86_64 + profile: Nexus 6 + script: echo 'Generated AVD snapshot for caching.' - name: launch android emulator & run android integration test uses: reactivecircus/android-emulator-runner@d94c3fbe4fe6a29e4a5ba47c12fb47677c73656b #pin@v2.28.0 @@ -93,7 +95,7 @@ jobs: fail-fast: false matrix: # 'beta' is flaky because of https://github.com/flutter/flutter/issues/124340 - sdk: ['stable'] + sdk: ["stable"] steps: - name: checkout uses: actions/checkout@v3 diff --git a/.github/workflows/web-example-ghpages.yml b/.github/workflows/web-example-ghpages.yml index 3de1885872..1e5adc9456 100644 --- a/.github/workflows/web-example-ghpages.yml +++ b/.github/workflows/web-example-ghpages.yml @@ -20,6 +20,7 @@ jobs: workingDir: flutter/example customArgs: --source-maps webRenderer: canvaskit + baseHref: "/sentry-dart/" - name: Upload source maps run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 7942e0deef..18b276958e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,23 @@ - This change was introduced in [relay/#1690](https://github.com/getsentry/relay/pull/1690) and released with [22.12.0](https://github.com/getsentry/relay/releases/tag/22.12.0) - Do not leak extensions of external classes ([#1576](https://github.com/getsentry/sentry-dart/pull/1576)) -## 7.9.0 +## Unreleased +### Fixes + +- Fixing memory leak issue in SentryFlutterPlugin (Android Plugin) ([#1588](https://github.com/getsentry/sentry-dart/pull/1588)) + +### Dependencies + +- Bump Android SDK from v6.25.2 to v6.28.0 ([#1586](https://github.com/getsentry/sentry-dart/pull/1586)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6280) + - [diff](https://github.com/getsentry/sentry-java/compare/6.25.2...6.28.0) +- Bump Cocoa SDK from v8.9.1 to v8.10.0 ([#1584](https://github.com/getsentry/sentry-dart/pull/1584), [#1606](https://github.com/getsentry/sentry-dart/pull/1606)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8100) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.9.1...8.10.0) + +## 7.9.0 + ### Features - Send trace origin ([#1534](https://github.com/getsentry/sentry-dart/pull/1534)) diff --git a/flutter/android/build.gradle b/flutter/android/build.gradle index 1a0d0cf12b..5e40acda85 100644 --- a/flutter/android/build.gradle +++ b/flutter/android/build.gradle @@ -60,6 +60,6 @@ android { } dependencies { - api 'io.sentry:sentry-android:6.25.2' + api 'io.sentry:sentry-android:6.28.0' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" } diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index 7035671ead..cc864f7333 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -11,11 +11,13 @@ import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result import io.sentry.Breadcrumb +import io.sentry.DateUtils +import io.sentry.Hint import io.sentry.HubAdapter +import io.sentry.Sentry import io.sentry.SentryEvent import io.sentry.SentryLevel -import io.sentry.Sentry -import io.sentry.DateUtils +import io.sentry.SentryOptions import io.sentry.android.core.ActivityFramesTracker import io.sentry.android.core.AppStartState import io.sentry.android.core.BuildConfig.VERSION_NAME @@ -26,7 +28,6 @@ import io.sentry.protocol.DebugImage import io.sentry.protocol.SdkVersion import io.sentry.protocol.SentryId import io.sentry.protocol.User -import io.sentry.protocol.Geo import java.io.File import java.lang.ref.WeakReference import java.util.Locale @@ -40,10 +41,6 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { private var framesTracker: ActivityFramesTracker? = null private var autoPerformanceTracingEnabled = false - private val flutterSdk = "sentry.dart.flutter" - private val androidSdk = "sentry.java.android.flutter" - private val nativeSdk = "sentry.native.android.flutter" - override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { context = flutterPluginBinding.applicationContext channel = MethodChannel(flutterPluginBinding.binaryMessenger, "sentry_flutter") @@ -127,9 +124,15 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { args.getIfNotNull("environment") { options.environment = it } args.getIfNotNull("release") { options.release = it } args.getIfNotNull("dist") { options.dist = it } - args.getIfNotNull("enableAutoSessionTracking") { options.isEnableAutoSessionTracking = it } - args.getIfNotNull("autoSessionTrackingIntervalMillis") { options.sessionTrackingIntervalMillis = it } - args.getIfNotNull("anrTimeoutIntervalMillis") { options.anrTimeoutIntervalMillis = it } + args.getIfNotNull("enableAutoSessionTracking") { + options.isEnableAutoSessionTracking = it + } + args.getIfNotNull("autoSessionTrackingIntervalMillis") { + options.sessionTrackingIntervalMillis = it + } + args.getIfNotNull("anrTimeoutIntervalMillis") { + options.anrTimeoutIntervalMillis = it + } args.getIfNotNull("attachThreads") { options.isAttachThreads = it } args.getIfNotNull("attachStacktrace") { options.isAttachStacktrace = it } args.getIfNotNull("enableAutoNativeBreadcrumbs") { @@ -183,12 +186,7 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { options.sdkVersion = sdkVersion options.sentryClientName = "$androidSdk/$VERSION_NAME" options.nativeSdkName = nativeSdk - - options.setBeforeSend { event, _ -> - setEventOriginTag(event) - addPackages(event, options.sdkVersion) - event - } + options.beforeSend = BeforeSendCallbackImpl(options.sdkVersion) args.getIfNotNull("connectionTimeoutMillis") { options.connectionTimeoutMillis = it } args.getIfNotNull("readTimeoutMillis") { options.readTimeoutMillis = it } @@ -408,30 +406,50 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { result.success("") } - private fun setEventOriginTag(event: SentryEvent) { - event.sdk?.let { - when (it.name) { - flutterSdk -> setEventEnvironmentTag(event, "flutter", "dart") - androidSdk -> setEventEnvironmentTag(event, environment = "java") - nativeSdk -> setEventEnvironmentTag(event, environment = "native") - else -> return - } + private class BeforeSendCallbackImpl( + private val sdkVersion: SdkVersion? + ) : SentryOptions.BeforeSendCallback { + override fun execute(event: SentryEvent, hint: Hint): SentryEvent { + setEventOriginTag(event) + addPackages(event, sdkVersion) + return event } } - private fun setEventEnvironmentTag(event: SentryEvent, origin: String = "android", environment: String) { - event.setTag("event.origin", origin) - event.setTag("event.environment", environment) - } - - private fun addPackages(event: SentryEvent, sdk: SdkVersion?) { - event.sdk?.let { - if (it.name == flutterSdk) { - sdk?.packageSet?.forEach { sentryPackage -> - it.addPackage(sentryPackage.name, sentryPackage.version) + companion object { + + private const val flutterSdk = "sentry.dart.flutter" + private const val androidSdk = "sentry.java.android.flutter" + private const val nativeSdk = "sentry.native.android.flutter" + private fun setEventOriginTag(event: SentryEvent) { + event.sdk?.let { + when (it.name) { + flutterSdk -> setEventEnvironmentTag(event, "flutter", "dart") + androidSdk -> setEventEnvironmentTag(event, environment = "java") + nativeSdk -> setEventEnvironmentTag(event, environment = "native") + else -> return } - sdk?.integrationSet?.forEach { integration -> - it.addIntegration(integration) + } + } + + private fun setEventEnvironmentTag( + event: SentryEvent, + origin: String = "android", + environment: String + ) { + event.setTag("event.origin", origin) + event.setTag("event.environment", environment) + } + + private fun addPackages(event: SentryEvent, sdk: SdkVersion?) { + event.sdk?.let { + if (it.name == flutterSdk) { + sdk?.packageSet?.forEach { sentryPackage -> + it.addPackage(sentryPackage.name, sentryPackage.version) + } + sdk?.integrationSet?.forEach { integration -> + it.addIntegration(integration) + } } } } diff --git a/flutter/example/integration_test/integration_test.dart b/flutter/example/integration_test/integration_test.dart index 4ffc5abbfb..ae42ae3945 100644 --- a/flutter/example/integration_test/integration_test.dart +++ b/flutter/example/integration_test/integration_test.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_print + import 'dart:async'; import 'dart:convert'; @@ -160,6 +162,7 @@ void main() { final uri = Uri.parse( 'https://sentry.io/api/0/projects/$org/$slug/events/$id/', ); + expect(authToken, isNotEmpty); final event = await fixture.poll(uri, authToken); expect(event, isNotNull); @@ -206,26 +209,31 @@ class Fixture { const maxRetries = 10; const initialDelay = Duration(seconds: 2); - const factor = 2; + const delayIncrease = Duration(seconds: 2); var retries = 0; var delay = initialDelay; while (retries < maxRetries) { try { + print("Trying to fetch $url [try $retries/$maxRetries]"); final response = await client.get( url, headers: {'Authorization': 'Bearer $authToken'}, ); + print("Response status code: ${response.statusCode}"); if (response.statusCode == 200) { return jsonDecode(utf8.decode(response.bodyBytes)); + } else if (response.statusCode == 401) { + print("Cannot fetch $url - invalid auth token."); + break; } } catch (e) { // Do nothing } finally { retries++; await Future.delayed(delay); - delay *= factor; + delay += delayIncrease; } } return null; diff --git a/flutter/example/ios/Podfile b/flutter/example/ios/Podfile index ca3292400a..313ea4a153 100644 --- a/flutter/example/ios/Podfile +++ b/flutter/example/ios/Podfile @@ -1,7 +1,5 @@ -wanted_project_target = '11.0' - # Uncomment this line to define a global platform for your project -platform :ios, wanted_project_target +platform :ios, '11.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -37,15 +35,6 @@ target 'Runner' do end post_install do |installer| - # remove after https://github.com/flutter/flutter/issues/124340 getting into all channels - installer.generated_projects.each do |project| - project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = wanted_project_target - end - end - end - installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) end diff --git a/flutter/ios/Classes/SentryFlutterPluginApple.swift b/flutter/ios/Classes/SentryFlutterPluginApple.swift index e5ae1a6c13..f531fda120 100644 --- a/flutter/ios/Classes/SentryFlutterPluginApple.swift +++ b/flutter/ios/Classes/SentryFlutterPluginApple.swift @@ -466,6 +466,7 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin { } private func fetchNativeAppStart(result: @escaping FlutterResult) { + #if os(iOS) || os(tvOS) guard let appStartMeasurement = PrivateSentrySDKOnly.appStartMeasurement else { print("warning: appStartMeasurement is null") result(nil) @@ -481,6 +482,10 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin { ] result(item) + #else + print("note: appStartMeasurement not available on this platform") + result(nil) + #endif } private var totalFrames: UInt = 0 diff --git a/flutter/ios/sentry_flutter.podspec b/flutter/ios/sentry_flutter.podspec index 2962a98cf2..0b0351bd44 100644 --- a/flutter/ios/sentry_flutter.podspec +++ b/flutter/ios/sentry_flutter.podspec @@ -6,13 +6,13 @@ Pod::Spec.new do |s| Sentry SDK for Flutter with support to native through sentry-cocoa. DESC s.homepage = 'https://sentry.io' - s.license = { :file => '../LICENSE' } + s.license = { :type => 'MIT', :file => '../LICENSE' } s.authors = "Sentry" s.source = { :git => "https://github.com/getsentry/sentry-dart.git", :tag => s.version.to_s } s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' - s.dependency 'Sentry/HybridSDK', '8.9.1' + s.dependency 'Sentry/HybridSDK', '8.10.0' s.ios.dependency 'Flutter' s.osx.dependency 'FlutterMacOS' s.ios.deployment_target = '11.0'