diff --git a/.circleci/config.yml b/.circleci/config.yml index 2865144efcc..c63f99a1b93 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -175,7 +175,7 @@ jobs: BUNDLE_PATH: .vendor macos: - xcode: 16.1 + xcode: 16.2 resource_class: macos.m1.medium.gen1 steps: @@ -191,12 +191,58 @@ jobs: name: Update rollout if needed command: ./scripts/deploys/update-android-rollout-if-needed + run-e2e-tests: + parallelism: 4 + + environment: + BUNDLE_PATH: .vendor # path to install gems and use for caching + + macos: + xcode: 16.2 + resource_class: m2pro.medium + + steps: + - attach_workspace: + at: ../workspace + - macos/preboot-simulator: + version: "18.2" + platform: "iOS" + device: "iPhone 16 Pro" + - run: + name: Install maestro + command: curl -Ls "https://get.maestro.mobile.dev" | bash + - run: + name: Install idb + command: brew tap facebook/fb && brew install idb-companion + - run: + name: Clear project dir + command: | + rm -rf /Users/distiller/project/* + - checkout + - setup-awscli + - run: + name: Set up maestro env + command: ./scripts/setup/setup-maestro-env.sh + - run: + name: Download app zip + command: aws s3 cp s3://artsy-citadel/eigen/builds/ios/Artsy-latest.zip ./Artsy.zip + - run: + name: Unzip the app + command: unzip Artsy.zip + - run: + name: Install app in booted sims + command: ./scripts/utils/install_app_in_booted_sims.sh + - run: + name: Run Maestro Tests + no_output_timeout: 25m + command: ./scripts/utils/run_maestro_shard.sh + deploy-nightly-beta: environment: BUNDLE_PATH: .vendor macos: - xcode: 16.1 + xcode: 16.2 resource_class: macos.m1.medium.gen1 steps: @@ -217,7 +263,7 @@ jobs: BUNDLE_PATH: .vendor macos: - xcode: 16.1 + xcode: 16.2 resource_class: macos.m1.medium.gen1 steps: @@ -309,7 +355,7 @@ jobs: BUNDLE_PATH: .vendor # path to install gems and use for caching macos: - xcode: 16.1 + xcode: 16.2 resource_class: macos.m1.medium.gen1 steps: @@ -383,7 +429,7 @@ jobs: BUNDLE_PATH: .vendor # path to install gems and use for caching macos: - xcode: 16.1 + xcode: 16.2 resource_class: macos.m1.medium.gen1 steps: @@ -423,7 +469,7 @@ jobs: - install-gems - install-cocoapods - macos/preboot-simulator: - version: "18.1" + version: "18.2" platform: "iOS" device: "iPhone 16 Pro" - build-app-ios @@ -518,8 +564,12 @@ workflows: only: - main jobs: + - build-test-app-ios - deploy-nightly-beta - run-nightly-tasks + - run-e2e-tests: + requires: + - build-test-app-ios flag-check: triggers: @@ -600,3 +650,11 @@ workflows: requires: - build-test-app-ios - build-test-app-android + + - run-e2e-tests: + filters: + branches: + only: + - beta-ios + requires: + - build-test-app-ios diff --git a/.gitignore b/.gitignore index f991d81b75e..66817f83912 100644 --- a/.gitignore +++ b/.gitignore @@ -151,6 +151,9 @@ tsconfig.tsbuildinfo # Temporary files created by Metro to check the health of the file watcher .metro-health-check* +# maestro test artifacts +.maestro/* + ######################################################### # Keep this rule last. # Keep these, so we can keep the folders these appear in. diff --git a/app.json b/app.json index 414cff43fc8..0e256659271 100644 --- a/app.json +++ b/app.json @@ -1,6 +1,6 @@ { "appName": "eigen", - "version": "8.74.0", + "version": "8.75.0", "isAndroidBeta": false, "slug": "eigen", "expo": { diff --git a/e2e/flows/onboarding/onboardingQuiz.js b/e2e/broken_flows/onboardingQuiz.js similarity index 100% rename from e2e/flows/onboarding/onboardingQuiz.js rename to e2e/broken_flows/onboardingQuiz.js diff --git a/e2e/flows/onboarding/onboardingQuiz.yml b/e2e/broken_flows/onboardingQuiz.yml similarity index 100% rename from e2e/flows/onboarding/onboardingQuiz.yml rename to e2e/broken_flows/onboardingQuiz.yml diff --git a/e2e/config.yml b/e2e/config.yml index 26021c094eb..86e2329dfbe 100644 --- a/e2e/config.yml +++ b/e2e/config.yml @@ -1,9 +1,4 @@ appId: ${MAESTRO_APP_ID} --- -- runFlow: - file: flows/onboarding/login.yml -- runFlow: - file: flows/onboarding/signupWithoutOnboarding.yml -# This flow is broken because of performance issues and hangs of the Art Taste Quiz Screen -# - runFlow: -# file: flows/onboarding/onboardingQuiz.yml +flows: + - "flows/*" diff --git a/e2e/flows/deeplinks.yml b/e2e/flows/deeplinks.yml new file mode 100644 index 00000000000..601785a866e --- /dev/null +++ b/e2e/flows/deeplinks.yml @@ -0,0 +1,23 @@ +appId: ${MAESTRO_APP_ID} +--- +- launchApp: + clearState: false + arguments: + email: ${MAESTRO_TEST_EMAIL} + password: ${MAESTRO_TEST_PASSWORD} +- assertVisible: "New Works for You" +- killApp +- openLink: + link: https://www.artsy.net/artist/kaws + autoVerify: false +- assertVisible: "Kaws" +- killApp +- openLink: + link: https://click.artsy.net/f/a/epPWiX0dJaHHMiXAnSrzSg~~/AAQRxQA~/RgRj95QSP0SQaHR0cHM6Ly93d3cuYXJ0c3kubmV0L2NvbGxlY3Rpb24vYXVjdGlvbi1oaWdobGlnaHRzP3V0bV9zb3VyY2U9YnJhemUmdXRtX21lZGl1bT1lbWFpbCZ1dG1fY2FtcGFpZ249bnRmJnV0bV90ZXJtPTYyMTRmYjlkZWI0ZWZkN2RiNjBjZTcwNDk1NWU4NDQ1VwNzcGNCCmIFEg8VYlEJKGBSEmJyaWJlY2sxQGdtYWlsLmNvbVgEAAAAfw~~ + autoVerify: false +- assertVisible: "Highlights at Auction This Week" +- killApp +- openLink: + link: "https://email-link.artsy.net/ls/click?upn=u001.XPbk8dXjc6dwd18Yq2uPM3eTWB4YbDUmeJTAXXTF513sTSKEFRIPxrFgGj8gd2LN9AcRIza5p2-2Fi0huqjv6GWgdnpwA-2FRyTEIYA-2BT1-2F0josxHnYTzJpKeJEmkfzDQlMEY9y6FknqDddY-2BKmm5qtTrTIF2hxySbdUboaYf5ofK-2Bj-2FgSolzBiLvvzai-2FmvjWbSpNpJHWATexQixwLakesmmZUBoRoLkCnxs59pBxJPbB3JZyP13cv77lbfn1WzJ86fiW-2B5MQcd2qss7wLGBI-2BZuy-2FxDkbFuDzpfYPDlaUs1-2BuyO9rqleUCwXPEuyWXZ3aGJrh-_dWIRBTTKlL2IhQqV5a1Cy38Sd4xylePmkTfQkXpdWOLZbG0vcWuzG-2BSSjZ7xaSUgPR0lxFBD0Zz4mpyMqiSRd7GblcbPN8eH0NYfUK6pmADWc2UoFXyJdeq-2F-2BOyOeWlS7O-2FOrHgB9-2BOGtV1nkIFPq97vJTNmWtBMDOt77SsursARR1LHG7CnzBEedV5yll40aEh9LTjA47-2BfpvonTwdxvYfd6ZWsZN2uRczvCs34ipSKUClLSzq0xCn8IjAixssMm2IreBk6LmNzHPC1YLqmZ77pv0qTxX095mxh8mk7f9rJJ8Tg1fyfRFriXGsQqgHlY-2BqTf2aAU6LS9NVXirqdp20-2BHXhzUZbaCnkQNASABts91iKoZaDeV4xnYuuo3L-2F8" + autoVerify: false +- assertVisible: "The artwork you were looking for isn't available." diff --git a/e2e/flows/login.yml b/e2e/flows/login.yml new file mode 100644 index 00000000000..764be5370c4 --- /dev/null +++ b/e2e/flows/login.yml @@ -0,0 +1,13 @@ +appId: ${MAESTRO_APP_ID} +--- +- launchApp: + clearState: false + arguments: + shouldSignOut: "true" +- assertVisible: "Sign up or log in" +- tapOn: "Email Input" +- inputText: ${MAESTRO_TEST_EMAIL} +- tapOn: "Continue.*" +- inputText: ${MAESTRO_TEST_PASSWORD} +- tapOn: "Continue.*" +- assertVisible: "New Works for You" diff --git a/e2e/flows/onboarding/login.yml b/e2e/flows/onboarding/login.yml deleted file mode 100644 index 7dc9535ca5d..00000000000 --- a/e2e/flows/onboarding/login.yml +++ /dev/null @@ -1,31 +0,0 @@ -appId: ${MAESTRO_APP_ID} ---- -- launchApp: - clearState: true - clearKeychain: true -- tapOn: "Log in.*" -- tapOn: "Continue with Email.*" -- inputText: ${MAESTRO_TEST_EMAIL} -- runFlow: - when: - platform: iOS - commands: - - tapOn: "Password show password button" -- runFlow: - when: - platform: Android - commands: - - tapOn: "password" -- inputText: ${MAESTRO_TEST_PASSWORD} -- tapOn: - id: "loginButton" -# Related to the issue https://github.com/mobile-dev-inc/maestro/issues/1227 -# it avoids getting stuck on iOS modal asking to store the password -- runFlow: - when: - platform: iOS - visible: "Would you like to save this password in your Keychain to use with apps and websites?" - commands: - - tapOn: - id: "Home Grabber" -- assertVisible: "New Works for You" diff --git a/e2e/flows/onboarding/signup.yml b/e2e/flows/onboarding/signup.yml deleted file mode 100644 index b9955ac828d..00000000000 --- a/e2e/flows/onboarding/signup.yml +++ /dev/null @@ -1,25 +0,0 @@ -appId: ${MAESTRO_APP_ID} ---- -- launchApp: - clearState: true - clearKeychain: true -- tapOn: "Sign up.*" -- tapOn: "Continue with Email.*" -- runScript: - file: signup.js -- inputText: ${output.signup.email} -- tapOn: "Next.*" -- inputText: ${output.signup.password} -- tapOn: "Next.*" -- inputText: "Test McTest" -- runFlow: - when: - platform: iOS - commands: - - tapOn: "checkbox of consent By checking this box, you consent to our Terms of Use, Privacy Policy, and Conditions of Sale." -- runFlow: - when: - platform: Android - commands: - - tapOn: "Accept terms and privacy policy, Check this element to accept Artsy's terms and privacy policy" -- tapOn: "Next.*" diff --git a/e2e/flows/onboarding/signup.js b/e2e/flows/signup.js similarity index 100% rename from e2e/flows/onboarding/signup.js rename to e2e/flows/signup.js diff --git a/e2e/flows/signup.yml b/e2e/flows/signup.yml new file mode 100644 index 00000000000..82e1a71bbe4 --- /dev/null +++ b/e2e/flows/signup.yml @@ -0,0 +1,21 @@ +appId: ${MAESTRO_APP_ID} +--- +- launchApp: + clearState: false + arguments: + shouldSignOut: "true" +- runScript: + file: signup.js +- assertVisible: "Sign up or log in" +- tapOn: "Email Input" +- inputText: ${output.signup.email} +- tapOn: "Continue.*" +- assertVisible: "Welcome to Artsy" +- inputText: ${output.signup.password} +- tapOn: "Continue.*" +- inputText: "Test McTest" +- runFlow: + commands: + - tapOn: + point: 15%,36% +- tapOn: "Continue.*" diff --git a/e2e/flows/onboarding/signupWithoutOnboarding.yml b/e2e/flows/signupWithoutOnboarding.yml similarity index 71% rename from e2e/flows/onboarding/signupWithoutOnboarding.yml rename to e2e/flows/signupWithoutOnboarding.yml index dd4f22bdf48..9ce5e6beb0e 100644 --- a/e2e/flows/onboarding/signupWithoutOnboarding.yml +++ b/e2e/flows/signupWithoutOnboarding.yml @@ -2,5 +2,6 @@ appId: ${MAESTRO_APP_ID} --- - runFlow: file: signup.yml +- assertVisible: "Ready to find art you love?" - tapOn: "Skip.*" - assertVisible: "New Works for You" diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 2f5dd10d59a..68c4ab6d070 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -47,6 +47,7 @@ GIT_REMOTE_ORIGIN_URL = `git config --get remote.origin.url`.chomp S3_PATH = 's3://artsy-citadel/eigen' S3_ANDROID_BUILDS_PATH = S3_PATH + '/builds/android/' +S3_IOS_BUILDS_PATH = S3_PATH + '/builds/ios/' import 'utility_fastlane.rb' import 'sentry_fastlane.rb' @@ -361,12 +362,6 @@ lane :create_android_apk do ) end -lane :s3_upload_android_build do |options| - app_version = options[:app_version] - app_path = options[:app_path] - sh('aws s3 cp ' + app_path + ' ' + S3_ANDROID_BUILDS_PATH + app_version.to_s + '.aab') -end - lane :select_android_build do |options| select_message = options[:select_message] || 'release' skip_download = options[:skip_download] || false diff --git a/fastlane/utility_fastlane.rb b/fastlane/utility_fastlane.rb index 9d662bab1a3..2c4f3192851 100644 --- a/fastlane/utility_fastlane.rb +++ b/fastlane/utility_fastlane.rb @@ -1,3 +1,5 @@ +require "pathname" + # Utility functions desc "Updates the version string in app.json" @@ -70,6 +72,49 @@ ) end +# Build and upload iOS and Android builds to S3 + +lane :s3_upload_ios_build do |options| + UI.message("Uploading iOS build to S3...") + + app_version = options[:app_version] + archive_root = options[:archive_root] || "../archives" + app_name = options[:app_name] || "Artsy" + + # Find latest archive + pattern = File.join(archive_root, "#{app_name}*.xcarchive") + matching_archives = Dir.glob(pattern) + + UI.user_error!("No .xcarchive found matching pattern #{pattern}") unless matching_archives.any? + + latest_archive = matching_archives.max_by { |f| File.mtime(f) } + app_path = File.join(latest_archive, "Products/Applications/#{app_name}.app") + UI.user_error!("App not found at #{app_path}") unless File.exist?(app_path) + + # Create zip + zip_name = "#{app_name}-#{app_version}.zip" + zip_path = File.join(archive_root, zip_name) + latest_zip_path = File.join(archive_root, "#{app_name}-latest.zip") + app_dir = File.dirname(app_path) + app_folder = File.basename(app_path) + zip_path_absolute = Pathname.new(zip_path).realpath.to_s rescue Pathname.new(zip_path).expand_path.to_s + + sh("cd '#{app_dir}' && zip -r '#{zip_path_absolute}' '#{app_folder}'") + FileUtils.cp(zip_path, latest_zip_path) + + # Upload both versioned and "latest" + sh("aws s3 cp #{zip_path} #{S3_IOS_BUILDS_PATH}#{zip_name}") + sh("aws s3 cp #{latest_zip_path} #{S3_IOS_BUILDS_PATH}#{app_name}-latest.zip") + + UI.success("✅ Uploaded #{zip_name} and latest to S3") +end + +lane :s3_upload_android_build do |options| + app_version = options[:app_version] + app_path = options[:app_path] + sh('aws s3 cp ' + app_path + ' ' + S3_ANDROID_BUILDS_PATH + app_version.to_s + '.aab') +end + lane :tag_and_push do |options| # Do a tag, we use a http git remote so we can have push access # as the default remote for circle is read-only diff --git a/ios/Artsy/Networking/API_Modules/ArtsyAPI+DeviceTokens.m b/ios/Artsy/Networking/API_Modules/ArtsyAPI+DeviceTokens.m index 9ea54b05ef0..b9b3cda3069 100644 --- a/ios/Artsy/Networking/API_Modules/ArtsyAPI+DeviceTokens.m +++ b/ios/Artsy/Networking/API_Modules/ArtsyAPI+DeviceTokens.m @@ -13,6 +13,13 @@ + (AFHTTPRequestOperation *)setAPNTokenForCurrentDevice:(NSString *)token succes if (token && name) { NSURLRequest *request = [ARRouter newSetDeviceAPNTokenRequest:token forDevice:name]; + + // TODO: Is it a problem that this can be nil? + // Should we fail loudly? Wait for the httpClient to be ready? + if (!request) { + return nil; + } + return [ArtsyAPI performRequest:request success:success failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) { if (failure) { failure(error); @@ -31,6 +38,11 @@ + (AFHTTPRequestOperation *)deleteAPNTokenForCurrentDeviceWithCompletion:(void ( return nil; } NSURLRequest *request = [ARRouter newDeleteDeviceRequest:token]; + + if (!request) { + completion(); + return nil; + } return [ArtsyAPI performRequest:request success:^ (id _) { completion(); } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) { diff --git a/ios/ArtsyTests/Supporting_Files/ARTestHelper.m b/ios/ArtsyTests/Supporting_Files/ARTestHelper.m index f6445edc6f4..6be9aa1a068 100644 --- a/ios/ArtsyTests/Supporting_Files/ARTestHelper.m +++ b/ios/ArtsyTests/Supporting_Files/ARTestHelper.m @@ -16,8 +16,8 @@ - (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions: { NSOperatingSystemVersion version = [NSProcessInfo processInfo].operatingSystemVersion; - NSAssert(version.majorVersion == 18 && version.minorVersion == 1, - @"The tests should be run on iOS 18.1, not %ld.%ld", version.majorVersion, version.minorVersion); + NSAssert(version.majorVersion == 18 && version.minorVersion == 2, + @"The tests should be run on iOS 18.2, not %ld.%ld", version.majorVersion, version.minorVersion); CGSize nativeResolution = [UIScreen mainScreen].nativeBounds.size; NSAssert([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone && CGSizeEqualToSize(nativeResolution, CGSizeMake(1206, 2622)), diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 10b1217743d..2dfc43dff21 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1668,6 +1668,8 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - react-native-launch-arguments (4.0.4): + - React - react-native-netinfo (11.3.2): - React-Core - react-native-pager-view (6.5.0): @@ -2443,6 +2445,7 @@ DEPENDENCIES: - react-native-get-random-values (from `../node_modules/react-native-get-random-values`) - react-native-in-app-review (from `../node_modules/react-native-in-app-review`) - react-native-keys (from `../node_modules/react-native-keys`) + - react-native-launch-arguments (from `../node_modules/react-native-launch-arguments`) - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)" - react-native-pager-view (from `../node_modules/react-native-pager-view`) - react-native-render-html (from `../node_modules/react-native-render-html`) @@ -2737,6 +2740,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-in-app-review" react-native-keys: :path: "../node_modules/react-native-keys" + react-native-launch-arguments: + :path: "../node_modules/react-native-launch-arguments" react-native-netinfo: :path: "../node_modules/@react-native-community/netinfo" react-native-pager-view: @@ -3002,6 +3007,7 @@ SPEC CHECKSUMS: react-native-get-random-values: d16467cf726c618e9c7a8c3c39c31faa2244bbba react-native-in-app-review: b3d1eed3d1596ebf6539804778272c4c65e4a400 react-native-keys: b8628f6db75d113f89bae4a69422e58b0bbcd373 + react-native-launch-arguments: 8ad8efa525a4c7cb72b3f69f05abfcba86f65dea react-native-netinfo: ce102083db558237dac20cf64172ef569ebe2dd9 react-native-pager-view: c8b6ddfc7747d272ad953e632f1296e98ad59e28 react-native-render-html: 5afc4751f1a98621b3009432ef84c47019dcb2bd diff --git a/package.json b/package.json index 9a0d20ad68e..14f2c854b06 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,8 @@ "setup:artsy": "./scripts/setup/setup-env-for-artsy", "setup:artsy:update!": "scripts/setup/update-env-for-artsy", "setup:oss": "./scripts/setup/setup-env-for-oss", + "setup:maestro": "./scripts/setup/setup-env-for-maestro", + "setup:maestro:update!": "scripts/setup/update-env-for-maestro", "setup:releases": "./scripts/setup/setup-env-for-artsy && ./scripts/setup/setup-env-for-releases", "setup:releases:update!": "scripts/setup/update-env-for-releases", "start": "concurrently 'yarn relay:watch' 'react-native start'", @@ -167,6 +169,7 @@ "react-native-in-app-review": "4.3.3", "react-native-keychain": "9.2.2", "react-native-keys": "0.7.11", + "react-native-launch-arguments": "4.0.4", "react-native-linear-gradient": "2.8.3", "react-native-localize": "2.1.3", "react-native-pager-view": "6.5.0", diff --git a/scripts/ci/build-for-tests-ios b/scripts/ci/build-for-tests-ios index d91bf82b204..05a213fa394 100755 --- a/scripts/ci/build-for-tests-ios +++ b/scripts/ci/build-for-tests-ios @@ -1,6 +1,8 @@ #!/usr/bin/env bash set -euxo pipefail -xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -configuration Debug -sdk iphonesimulator build -destination platform="$DEVICE_HOST_PLAT",OS="$DEVICE_HOST_OS",name="$DEVICE_HOST_NAME" -derivedDataPath "$DERIVED_DATA_PATH" GCC_PREPROCESSOR_DEFINITIONS='$(inherited)' | +xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -configuration Debug -sdk iphonesimulator build -destination platform="$DEVICE_HOST_PLAT",OS="$DEVICE_HOST_OS",name="$DEVICE_HOST_NAME" -derivedDataPath "$DERIVED_DATA_PATH" ONLY_ACTIVE_ARCH=YES GCC_PREPROCESSOR_DEFINITIONS='$(inherited)' | tee ./xcode_build_raw.log | bundle exec xcpretty -c + +./scripts/ci/upload_sim_app \ No newline at end of file diff --git a/scripts/ci/ci-setup-export-vars b/scripts/ci/ci-setup-export-vars index e6e4bb6766d..602c5ff5f20 100755 --- a/scripts/ci/ci-setup-export-vars +++ b/scripts/ci/ci-setup-export-vars @@ -10,7 +10,7 @@ export LOCAL_BRANCH export WORKSPACE="ios/Artsy.xcworkspace" export SCHEME="Artsy" export DEVICE_HOST_PLAT="iOS Simulator" -export DEVICE_HOST_OS="18.1" +export DEVICE_HOST_OS="18.2" export DEVICE_HOST_NAME="iPhone 16 Pro" export DERIVED_DATA_PATH="derived_data" export CONFIGURATION="Release" diff --git a/scripts/ci/upload_sim_app b/scripts/ci/upload_sim_app new file mode 100755 index 00000000000..01c40dc187c --- /dev/null +++ b/scripts/ci/upload_sim_app @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -euxo pipefail + +APP_NAME="Artsy" +DERIVED_DATA_PATH="${DERIVED_DATA_PATH:-derived_data}" +S3_DEST="s3://artsy-citadel/eigen/builds/ios/${APP_NAME}-latest.zip" + +# Find the .app bundle +APP_PATH=$(find "$DERIVED_DATA_PATH/Build/Products/Debug-iphonesimulator" -name "${APP_NAME}.app" -type d | head -n1) + +if [ -z "$APP_PATH" ]; then + echo "❌ .app not found!" >&2 + exit 1 +fi + +echo "✅ Found app at: $APP_PATH" + +# Zip the .app bundle +ZIP_NAME="${APP_NAME}-latest.zip" +cd "$(dirname "$APP_PATH")" +zip -r "$ZIP_NAME" "$(basename "$APP_PATH")" + +# Upload to S3 +aws s3 cp "$ZIP_NAME" "$S3_DEST" + +echo "✅ Uploaded $ZIP_NAME to $S3_DEST" diff --git a/scripts/setup/setup-env-for-maestro b/scripts/setup/setup-env-for-maestro new file mode 100755 index 00000000000..0c68823b07c --- /dev/null +++ b/scripts/setup/setup-env-for-maestro @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euxo pipefail + +aws s3 cp s3://artsy-citadel/eigen/.env.maestro .env.maestro diff --git a/scripts/setup/update-env-for-maestro b/scripts/setup/update-env-for-maestro new file mode 100755 index 00000000000..68f5f99ce77 --- /dev/null +++ b/scripts/setup/update-env-for-maestro @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -euxo pipefail + + +read -p "Are you sure you want to update the env vars in S3? " -n 1 -r + +if [[ $REPLY =~ ^[Yy]$ ]] +then + aws s3 cp .env.maestro s3://artsy-citadel/eigen/.env.maestro + + RED='\033[0;31m' + RST='\033[0m' + + printf "${RED}Don't forget to update on 1Password and CircleCI too!${RST}\n" +fi diff --git a/scripts/utils/install_app_in_booted_sims.sh b/scripts/utils/install_app_in_booted_sims.sh new file mode 100755 index 00000000000..2013749c769 --- /dev/null +++ b/scripts/utils/install_app_in_booted_sims.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Path to your .app file +APP_PATH="./Artsy.app" + +# Get a list of all booted simulator UUIDs +BOOTED_SIMULATORS=$(xcrun simctl list devices booted | grep -oE "[A-F0-9-]{36}") + +# Install the .app file on each booted simulator +for SIMULATOR in $BOOTED_SIMULATORS; do + echo "Installing $APP_PATH on simulator $SIMULATOR..." + xcrun simctl install $SIMULATOR "$APP_PATH" +done + +echo "Installation completed on all booted simulators!" \ No newline at end of file diff --git a/scripts/utils/run_maestro_shard.sh b/scripts/utils/run_maestro_shard.sh new file mode 100755 index 00000000000..1626927e3f9 --- /dev/null +++ b/scripts/utils/run_maestro_shard.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +TEST_FILES=$(find e2e/flows -name "*.yml" | sort | awk "NR % ${CIRCLE_NODE_TOTAL} == ${CIRCLE_NODE_INDEX}") + +EXIT_CODE=0 + +for TEST_FILE in $TEST_FILES; do + echo "Running test: $TEST_FILE" + maestro test "$TEST_FILE" @.env.maestro + if [ $? -ne 0 ]; then + EXIT_CODE=1 + fi +done + +echo "Final Exit Code: $EXIT_CODE" +exit $EXIT_CODE \ No newline at end of file diff --git a/src/app/App.tsx b/src/app/App.tsx index 59c57164995..aea52d460bf 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -3,6 +3,7 @@ import * as Sentry from "@sentry/react-native" import { Navigation } from "app/Navigation/Navigation" import { GlobalStore, unsafe__getEnvironment, unsafe_getDevToggle } from "app/store/GlobalStore" import { DevMenuWrapper } from "app/system/devTools/DevMenu/DevMenuWrapper" +import { useMaestroInitialization } from "app/system/devTools/useMaestroInitialization" import { useRageShakeDevMenu } from "app/system/devTools/useRageShakeDevMenu" import { setupSentry } from "app/system/errorReporting/setupSentry" import { usePurgeCacheOnAppUpdate } from "app/system/relay/usePurgeCacheOnAppUpdate" @@ -64,6 +65,7 @@ if (UIManager.setLayoutAnimationEnabledExperimental) { const Main = () => { useRageShakeDevMenu() + useMaestroInitialization() useEffect(() => { const oss = Keys.OSS diff --git a/src/app/Scenes/Onboarding/Auth2/scenes/LoginPasswordStep.tsx b/src/app/Scenes/Onboarding/Auth2/scenes/LoginPasswordStep.tsx index 2597e94c553..98429812169 100644 --- a/src/app/Scenes/Onboarding/Auth2/scenes/LoginPasswordStep.tsx +++ b/src/app/Scenes/Onboarding/Auth2/scenes/LoginPasswordStep.tsx @@ -120,6 +120,7 @@ const LoginPasswordStepForm: React.FC = () => { { { + const isLoggedIn = GlobalStore.useAppState((state) => !!state.auth.userAccessToken) + const isHydrated = GlobalStore.useAppState((state) => state.sessionState.isHydrated) + + useEffect(() => { + if (!ArtsyNativeModule.isBetaOrDev || !isHydrated) { + return + } + + const args = LaunchArguments.value() + const email = args.email + const password = args.password + const shouldSignOut = args.shouldSignOut + + if (email && password) { + GlobalStore.actions.auth.signIn({ + oauthProvider: "email", + oauthMode: "email", + email, + password, + }) + } else if (shouldSignOut && isLoggedIn) { + GlobalStore.actions.auth.signOut() + } + }, [isHydrated]) +} diff --git a/yarn.lock b/yarn.lock index b15d5b77760..ecf2a89b79e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2005,7 +2005,7 @@ "@babel/parser" "^7.27.0" "@babel/types" "^7.27.0" -"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3": +"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3", "@babel/traverse@^7.25.3": version "7.26.4" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.4.tgz#ac3a2a84b908dde6d463c3bfa2c5fdc1653574bd" integrity sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w== @@ -2047,19 +2047,6 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/traverse@^7.25.3": - version "7.26.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.4.tgz#ac3a2a84b908dde6d463c3bfa2c5fdc1653574bd" - integrity sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w== - dependencies: - "@babel/code-frame" "^7.26.2" - "@babel/generator" "^7.26.3" - "@babel/parser" "^7.26.3" - "@babel/template" "^7.25.9" - "@babel/types" "^7.26.3" - debug "^4.3.1" - globals "^11.1.0" - "@babel/traverse@^7.25.7": version "7.25.7" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.7.tgz#83e367619be1cab8e4f2892ef30ba04c26a40fa8" @@ -14389,6 +14376,11 @@ react-native-keys@0.7.11: walk-sync "^3.0.0" xml2js "^0.6.2" +react-native-launch-arguments@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/react-native-launch-arguments/-/react-native-launch-arguments-4.0.4.tgz#5cdf12265b50bf98ad05c87f849da99e07ef5500" + integrity sha512-cB7Sy9m9MX5MvNJliSHEMFWRScSbr95gFuGWnaINBoIK9sP8hloKqhn0vKUHrMQGmNsC1PcpyeiniBqTiU9d5g== + react-native-linear-gradient@2.8.3: version "2.8.3" resolved "https://registry.yarnpkg.com/react-native-linear-gradient/-/react-native-linear-gradient-2.8.3.tgz#9a116649f86d74747304ee13db325e20b21e564f" @@ -15888,16 +15880,7 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -16011,7 +15994,7 @@ stringify-entities@^3.1.0: character-entities-legacy "^1.0.0" xtend "^4.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -16025,13 +16008,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" @@ -17569,7 +17545,7 @@ word-wrap@^1.2.3, word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -17587,15 +17563,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"