diff --git a/.github/workflows/flutter.yml b/.github/workflows/flutter.yml index 02d9a1f012..7a4ee5df84 100644 --- a/.github/workflows/flutter.yml +++ b/.github/workflows/flutter.yml @@ -188,7 +188,7 @@ jobs: - uses: actions/checkout@v4 - name: ktlint - uses: ScaCap/action-ktlint@0ff81efa49425bd0df46caabd8005aafdc8f2cf2 # pin@1.8.0 + uses: ScaCap/action-ktlint@38262d0fb8bff43ddafc8b3c04bce6e6c7263319 # pin@1.8.1 with: github_token: ${{ secrets.github_token }} reporter: github-pr-review diff --git a/.github/workflows/testflight.yml b/.github/workflows/testflight.yml new file mode 100644 index 0000000000..3e05b6afbb --- /dev/null +++ b/.github/workflows/testflight.yml @@ -0,0 +1,57 @@ +name: Upload to Testflight +on: + push: + branches: + - main + - release/** + pull_request: + paths: + - '.github/workflows/testflight.yml' + +jobs: + upload_to_testflight: + name: Build and Upload to Testflight + runs-on: macos-13 + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@48cafc24713cca54bbe03cdc3a423187d413aafa # pin@v2.10.0 + - run: xcodes select 15.0.1 + - uses: ruby/setup-ruby@250fcd6a742febb1123a77a841497ccaa8b9e939 # pin@v1.152.0 + with: + ruby-version: '2.7.5' + bundler-cache: true + + - name: flutter + working-directory: ./flutter/example + run: | + flutter upgrade + flutter pub get + flutter build ios --no-codesign + + - name: Install Fastlane + working-directory: ./flutter/example/ios + run: bundle install + + - name: Bump, Build & Upload App to TestFlight + working-directory: ./flutter/example/ios + env: + APP_STORE_CONNECT_KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }} + APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }} + APP_STORE_CONNECT_KEY: ${{ secrets.APP_STORE_CONNECT_KEY }} + FASTLANE_BUNDLE_VERSION: ${{ github.run_number }} + FASTLANE_KEYCHAIN_PASSWORD: ${{ secrets.FASTLANE_KEYCHAIN_PASSWORD }} + MATCH_GIT_PRIVATE_KEY: ${{ secrets.MATCH_GIT_PRIVATE_KEY }} + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + MATCH_USERNAME: ${{ secrets.MATCH_USERNAME }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_LOG_LEVEL: DEBUG + run: | + bundle exec fastlane bump_build_number + bundle exec fastlane build_release + bundle exec fastlane upload_testflight + + - name: Upload Symbols to Sentry + working-directory: ./flutter/example + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + run: flutter packages pub run sentry_dart_plugin \ No newline at end of file diff --git a/flutter/example/ios/Flutter/AppFrameworkInfo.plist b/flutter/example/ios/Flutter/AppFrameworkInfo.plist index 1bdf525f9f..b7506dd05b 100644 --- a/flutter/example/ios/Flutter/AppFrameworkInfo.plist +++ b/flutter/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/flutter/example/ios/Gemfile b/flutter/example/ios/Gemfile new file mode 100644 index 0000000000..7a118b49be --- /dev/null +++ b/flutter/example/ios/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gem "fastlane" diff --git a/flutter/example/ios/Podfile b/flutter/example/ios/Podfile index 55ddd63416..8cb4ddb285 100644 --- a/flutter/example/ios/Podfile +++ b/flutter/example/ios/Podfile @@ -44,7 +44,7 @@ post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) target.build_configurations.each do |config| - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0' + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0' end end end diff --git a/flutter/example/ios/Runner.xcodeproj/project.pbxproj b/flutter/example/ios/Runner.xcodeproj/project.pbxproj index 29c58327af..ac52d1dcc4 100644 --- a/flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -460,7 +460,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -483,6 +483,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -491,7 +492,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.example; + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.sample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -518,7 +519,7 @@ MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.example.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.sample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_EMIT_LOC_STRINGS = NO; @@ -547,7 +548,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.2; MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.example.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.sample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; @@ -574,7 +575,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.2; MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.example.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.sample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; @@ -631,7 +632,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -681,7 +682,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -706,6 +707,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -714,7 +716,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.example; + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.sample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -737,6 +739,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -745,7 +748,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.example; + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.sample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/flutter/example/ios/Runner/Info.plist b/flutter/example/ios/Runner/Info.plist index 91d89f84d6..1023c8c85e 100644 --- a/flutter/example/ios/Runner/Info.plist +++ b/flutter/example/ios/Runner/Info.plist @@ -2,6 +2,10 @@ + ITSAppUsesNonExemptEncryption + + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable @@ -22,6 +26,8 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + UIApplicationSupportsIndirectInputEvents + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -41,9 +47,5 @@ UIViewControllerBasedStatusBarAppearance - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - diff --git a/flutter/example/ios/fastlane/Appfile b/flutter/example/ios/fastlane/Appfile new file mode 100644 index 0000000000..1f85333eac --- /dev/null +++ b/flutter/example/ios/fastlane/Appfile @@ -0,0 +1,7 @@ +app_identifier("io.sentry.flutter.sample") # The bundle identifier of your app + +itc_team_id("96157806") # App Store Connect Team ID +team_id("97JCY7859U") # Developer Portal Team ID + +# For more information about the Appfile, see: +# https://docs.fastlane.tools/advanced/#appfile diff --git a/flutter/example/ios/fastlane/Fastfile b/flutter/example/ios/fastlane/Fastfile new file mode 100644 index 0000000000..e14ca13d3c --- /dev/null +++ b/flutter/example/ios/fastlane/Fastfile @@ -0,0 +1,95 @@ +default_platform(:ios) + +platform :ios do + + desc "Bump Build Number" + lane :bump_build_number do + fetch_api_key() + + version_string = get_version_number(xcodeproj: "./Runner.xcodeproj") + version_parts = version_string.split(".") + + # Remove last digit if necessary + if version_parts.length > 3 + version_parts.pop + end + + new_version = version_parts.join(".") + + increment_version_number( + version_number: new_version, + xcodeproj: "./Runner.xcodeproj" + ) + increment_build_number( + build_number: latest_testflight_build_number + 1, + xcodeproj: "./Runner.xcodeproj" + ) + end + + desc "Build Release" + lane :build_release do + + setup_ci + + disable_automatic_code_signing + + sync_code_signing( + type: "development", + readonly: true, + app_identifier: ["io.sentry.flutter.sample"] + ) + + sync_code_signing( + type: "appstore", + readonly: true, + app_identifier: ["io.sentry.flutter.sample"] + ) + + update_project_provisioning( + xcodeproj: "Runner.xcodeproj", + target_filter: "Runner", + profile: ENV["sigh_io.sentry.flutter.sample_appstore_profile-path"], + build_configuration: "Release" + ) + + build_app( + workspace: "Runner.xcworkspace", + scheme: "Runner", + configuration: "Release", + clean: true, + include_symbols: true, + export_method: "app-store", + output_directory:"./build/", + export_options: { + method: "app-store", + provisioningProfiles: { + "io.sentry.flutter.sample" => ENV["sigh_io.sentry.flutter.sample_appstore_profile-name"] + } + }, + codesigning_identity: ENV["sigh_io.sentry.flutter.sample_appstore_certificate-name"], + output_name: "sentry_flutter_sample.ipa" + ) + + delete_keychain( + name: "fastlane_tmp_keychain" + ) unless is_ci + end + + desc "Upload to TestFlight" + lane :upload_testflight do + fetch_api_key() + testflight( + skip_waiting_for_build_processing: true, + ipa: "./build/sentry_flutter_sample.ipa" + ) + end + + desc "Fetch ASC API Key" + lane :fetch_api_key do + app_store_connect_api_key( + key_id: ENV["APP_STORE_CONNECT_KEY_ID"], + issuer_id: ENV["APP_STORE_CONNECT_ISSUER_ID"], + key_content: ENV["APP_STORE_CONNECT_KEY"] + ) + end +end diff --git a/flutter/example/ios/fastlane/Matchfile b/flutter/example/ios/fastlane/Matchfile new file mode 100644 index 0000000000..054fef1c37 --- /dev/null +++ b/flutter/example/ios/fastlane/Matchfile @@ -0,0 +1,5 @@ +git_url("git@github.com:getsentry/codesigning.git") +storage_mode("git") +username("bot@getsentry.com") # Your Apple Developer Portal username + +# The docs are available on https://docs.fastlane.tools/actions/match diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index 26bbdce6d8..64bfba90e3 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -478,12 +478,7 @@ class MainScaffold extends StatelessWidget { TooltipButton( onPressed: () async { final id = await Sentry.captureMessage('UserFeedback'); - // ignore: use_build_context_synchronously - if (!context.isMounted) { - return; - } - - // ignore: use_build_context_synchronously + if (!context.mounted) return; await showDialog( context: context, builder: (context) { @@ -906,12 +901,7 @@ Future makeWebRequest(BuildContext context) async { await transaction.finish(status: const SpanStatus.ok()); - // ignore: use_build_context_synchronously - if (!context.isMounted) { - return; - } - - // ignore: use_build_context_synchronously + if (!context.mounted) return; await showDialog( context: context, builder: (context) { @@ -957,12 +947,7 @@ Future makeWebRequestWithDio(BuildContext context) async { await span.finish(); } - // ignore: use_build_context_synchronously - if (!context.isMounted) { - return; - } - - // ignore: use_build_context_synchronously + if (!context.mounted) return; await showDialog( context: context, builder: (context) { @@ -992,12 +977,7 @@ Future showDialogWithTextAndImage(BuildContext context) async { final text = await DefaultAssetBundle.of(context).loadString('assets/lorem-ipsum.txt'); - // ignore: use_build_context_synchronously - if (!context.isMounted) { - return; - } - - // ignore: use_build_context_synchronously + if (!context.mounted) return; await showDialog( context: context, // gets tracked if using SentryNavigatorObserver