diff --git a/.depcheckrc.yml b/.depcheckrc.yml index 5a506bc7a51..e8fe7a446ec 100644 --- a/.depcheckrc.yml +++ b/.depcheckrc.yml @@ -4,6 +4,8 @@ ignores: - 'webpack-cli' - '@react-native-community/datetimepicker' - '@react-native-community/slider' + - 'patch-package' + - '@lavamoat/allow-scripts' # This is used on the patch for TokenRatesController of Assets controllers, for we to be able to use the last version of it - cockatiel diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f13a32ae47f..6dc2626eae8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,16 +5,22 @@ on: pull_request: merge_group: types: [checks_requested] - + jobs: - setup: - runs-on: ubuntu-20.04 + check-diff: + runs-on: macos-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version-file: '.nvmrc' cache: yarn + - uses: ruby/setup-ruby@a6e6f86333f0a2523ece813039b8b4be04560854 #v1 + with: + ruby-version: '3.1.5' + bundler-cache: true + env: + BUNDLE_GEMFILE: ios/Gemfile - name: Determine whether the current PR is a draft id: set-is-draft if: github.event_name == 'pull_request' && github.event.pull_request.number @@ -27,7 +33,7 @@ jobs: run: printf '%s\n\n%s' '@metamask:registry=https://npm.pkg.github.com' "//npm.pkg.github.com/:_authToken=${PACKAGE_READ_TOKEN}" > .npmrc env: PACKAGE_READ_TOKEN: ${{ secrets.PACKAGE_READ_TOKEN }} - - run: yarn setup --node + - run: yarn setup - name: Require clean working directory shell: bash run: | @@ -39,7 +45,6 @@ jobs: fi dedupe: runs-on: ubuntu-20.04 - needs: setup steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 @@ -58,7 +63,6 @@ jobs: fi scripts: runs-on: ubuntu-20.04 - needs: setup strategy: matrix: scripts: @@ -86,7 +90,6 @@ jobs: fi unit-tests: runs-on: ubuntu-20.04 - needs: setup strategy: matrix: shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] @@ -156,7 +159,6 @@ jobs: js-bundle-size-check: runs-on: ubuntu-20.04 - needs: setup steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 @@ -270,7 +272,7 @@ jobs: runs-on: ubuntu-20.04 needs: [ - setup, + check-diff, dedupe, scripts, unit-tests, diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index f577c5ba755..a95001cb1c6 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -4,21 +4,20 @@ on: branches: main pull_request: - jobs: docker: runs-on: ubuntu-latest steps: - name: Set up QEMU - uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3 + uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3 + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3 - uses: actions/checkout@v3 - name: Build and load - uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 + uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 with: context: . file: scripts/docker/Dockerfile diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 3c02108f3ce..00000000000 --- a/Gemfile +++ /dev/null @@ -1,10 +0,0 @@ -source 'https://rubygems.org' - -# Recommended to use http://rbenv.org/ to install and use this version -ruby '>= 3.1.5' - -# Allow minor version updates up to but excluding 2.0.0 -gem 'cocoapods', '~> 1.12' - -# Allow all version updates up to but excluding 7.1.0 -gem 'activesupport', '>= 6.1.7.3', '< 7.1.0' diff --git a/bitrise.yml b/bitrise.yml index cae5c05edf1..a30098cbeaa 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -253,7 +253,7 @@ workflows: - content: |- #!/usr/bin/env bash echo "Gems being installed with bundler gem" - bundle install + bundle install --gemfile=ios/Gemfile echo "Node $NODE_VERSION being installed" set -e diff --git a/docs/readme/environment.md b/docs/readme/environment.md index e5e36abb22f..33c61ad3f2f 100644 --- a/docs/readme/environment.md +++ b/docs/readme/environment.md @@ -30,7 +30,7 @@ Install ruby version defined in the file `.ruby-version` Install [`bundler`](https://bundler.io/) gem to manage and install gems such as Cocoapods. The `bundle install` command, which is run during `yarn setup` handles installing gem versions as specified in the project's `GemFile` ```bash -gem install bundler -v 2.5.8 && bundle install +gem install bundler -v 2.5.8 && bundle install --gemfile=ios/Gemfile ``` ### Xcode diff --git a/ios/Gemfile b/ios/Gemfile index 1afd2cce673..3c02108f3ce 100644 --- a/ios/Gemfile +++ b/ios/Gemfile @@ -1,7 +1,10 @@ -# frozen_string_literal: true +source 'https://rubygems.org' -source "https://rubygems.org" +# Recommended to use http://rbenv.org/ to install and use this version +ruby '>= 3.1.5' -git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } +# Allow minor version updates up to but excluding 2.0.0 +gem 'cocoapods', '~> 1.12' -# gem "rails" +# Allow all version updates up to but excluding 7.1.0 +gem 'activesupport', '>= 6.1.7.3', '< 7.1.0' diff --git a/Gemfile.lock b/ios/Gemfile.lock similarity index 100% rename from Gemfile.lock rename to ios/Gemfile.lock diff --git a/ios/MetaMask.xcodeproj/project.pbxproj b/ios/MetaMask.xcodeproj/project.pbxproj index 4670dbe9385..33f3d02a0fe 100644 --- a/ios/MetaMask.xcodeproj/project.pbxproj +++ b/ios/MetaMask.xcodeproj/project.pbxproj @@ -1555,11 +1555,7 @@ ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "$(inherited)"; OTHER_CPLUSPLUSFLAGS = "$(inherited)"; - OTHER_LDFLAGS = ( - "$(inherited)", - "-Wl", - "-ld_classic", - ); + OTHER_LDFLAGS = "$(inherited)"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; }; @@ -1603,11 +1599,7 @@ MTL_ENABLE_DEBUG_INFO = NO; OTHER_CFLAGS = "$(inherited)"; OTHER_CPLUSPLUSFLAGS = "$(inherited)"; - OTHER_LDFLAGS = ( - "$(inherited)", - "-Wl", - "-ld_classic", - ); + OTHER_LDFLAGS = "$(inherited)"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/ios/Podfile b/ios/Podfile index d9ae91f6b43..4282bf754f0 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -96,11 +96,6 @@ def common_target_logic # use_frameworks! pod 'Permission-BluetoothPeripheral', :path => '../node_modules/react-native-permissions/ios/BluetoothPeripheral' pod 'GzipSwift' - - # Adding OpenSSL-Universal to fix build issues in react-native-quick-crypto - # Ref: https://github.com/margelo/react-native-quick-crypto/issues/121 - # Ref: https://stackoverflow.com/questions/29082174/ios-xcode-build-failing-due-to-openssl/76455587#76455587 - pod 'OpenSSL-Universal', :modular_headers => true, :configurations => ['Release'] end target 'MetaMask' do diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 8c3ab2ee96c..54197b990a6 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -825,7 +825,6 @@ DEPENDENCIES: - GzipSwift - lottie-ios (from `../node_modules/lottie-ios`) - lottie-react-native (from `../node_modules/lottie-react-native`) - - OpenSSL-Universal - OpenSSL-Universal (= 1.1.1100) - Permission-BluetoothPeripheral (from `../node_modules/react-native-permissions/ios/BluetoothPeripheral`) - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) @@ -1303,6 +1302,6 @@ SPEC CHECKSUMS: Yoga: 6f5ab94cd8b1ecd04b6e973d0bc583ede2a598cc YogaKit: f782866e155069a2cca2517aafea43200b01fd5a -PODFILE CHECKSUM: 3514934ac1830af4ca844ba018f38720a48d006b +PODFILE CHECKSUM: 0e9407472193b5f7d5cfe4715c8f1f2c2fed42e5 COCOAPODS: 1.15.2 diff --git a/package.json b/package.json index e6fc641f585..93540e0b873 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "watch:clean": "./scripts/build.sh watcher clean", "clean:ios": "rm -rf ios/build", "pod:install": "command -v pod && bundle exec pod install --project-directory=ios || echo \"pod command not found. Skipping pod install\"", + "gem:bundle:install": "bundle install --gemfile=ios/Gemfile", "clean:ppom": "rm -rf ppom/dist ppom/node_modules app/lib/ppom/ppom.html.js", "clean:android": "rm -rf android/app/build", "clean:node": "rm -rf node_modules && yarn --frozen-lockfile", @@ -18,7 +19,6 @@ "lint:tsc": "tsc --project ./tsconfig.json", "format": "prettier '**/*.{js,ts,tsx,json,feature}' --write", "setup": "yarn clean && node scripts/setup.mjs", - "setup:node": "yarn allow-scripts && yarn patch-package", "setup:e2e": "cd wdio && yarn install", "setup:detox-e2e": "./scripts/detox-setup.sh", "start:ios": "./scripts/build.sh ios debug", diff --git a/scripts/docker/Dockerfile b/scripts/docker/Dockerfile index bf75d52672d..3ef506222ca 100644 --- a/scripts/docker/Dockerfile +++ b/scripts/docker/Dockerfile @@ -31,7 +31,7 @@ RUN bash -c "[ ${GID} != \"1000\" ] && groupadd -g ${GID} -U node userz || true && echo '%sudo ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers # install ruby deps -COPY --chown=${UID}:${GID} Gemfile* .ruby-version /tmp/app/ +COPY --chown=${UID}:${GID} ios/Gemfile* .ruby-version /tmp/app/ WORKDIR /tmp/app #### @@ -58,7 +58,10 @@ RUN bash -c 'eval "$(/home/node/.rbenv/bin/rbenv init -)" \ && bundle install' # fix broken ipv6 on nodejs v20 -env NODE_OPTIONS="--no-network-family-autoselection --trace-warnings" -env PATH=/home/node/.rbenv/shims/:$PATH +ENV NODE_OPTIONS="--no-network-family-autoselection --trace-warnings" +# minimize ruby memory usage +ENV MALLOC_ARENA_MAX=1 +ENV PATH=/home/node/.rbenv/shims/:$PATH +ENV LANG=en_US.UTF-8 WORKDIR /app diff --git a/scripts/setup.mjs b/scripts/setup.mjs index 10f2bd6ae22..1831d495670 100644 --- a/scripts/setup.mjs +++ b/scripts/setup.mjs @@ -4,12 +4,27 @@ import { $ } from 'execa'; import { Listr } from 'listr2'; import path from 'path'; -const IS_OSX = process.platform === 'darwin'; -const input = process.argv.slice(2)?.[0]; -// iOS builds are enabled by default on macOS only but can be enabled explicitly -const BUILD_IOS = input === '--build-ios' || IS_OSX; -const IS_NODE = input === '--node'; const IS_CI = process.env.CI; +const IS_OSX = process.platform === 'darwin'; +// iOS builds are enabled by default on macOS only but can be enabled or disabled explicitly +let BUILD_IOS = IS_OSX; +let IS_NODE = false; +const args = process.argv.slice(2) || []; +for (const arg of args) { + switch (arg) { + case '--build-ios': + BUILD_IOS = true; + continue; + case '--no-build-ios': + BUILD_IOS = false; + continue; + case '--node': + IS_NODE = true; + continue; + default: + throw new Error(`Unrecognized CLI arg ${arg}`); + } +} const rendererOptions = { collapseErrors: false, @@ -21,32 +36,68 @@ const rendererOptions = { /* * TODO: parse example env file and add missing variables to existing .js.env */ -const copyEnvVarsTask = { - title: 'Copy env vars', - task: async (_, task) => { +const copyAndSourceEnvVarsTask = { + title: 'Copy and source environment variables', + task: (_, task) => { if (IS_CI) { - task.skip('CI detected'); - } else { - const envFiles = ['.js.env', '.ios.env', '.android.env', '.e2e.env']; - envFiles.forEach((envFileName) => { - try { - fs.copyFileSync( - `${envFileName}.example`, - envFileName, - fs.constants.COPYFILE_EXCL, - ); - } catch (err) { - // Ignore if file already exists - return; - } - }); + return task.skip('Skipping copying and sourcing environment variables.'); } + + return task.newListr( + [ + { + title: 'Copy env vars', + task: async () => { + const envFiles = [ + '.js.env', + '.ios.env', + '.android.env', + '.e2e.env', + ]; + envFiles.forEach((envFileName) => { + try { + fs.copyFileSync( + `${envFileName}.example`, + envFileName, + fs.constants.COPYFILE_EXCL, + ); + } catch (err) { + // Ignore if file already exists + return; + } + }); + }, + }, + { + title: 'Source env vars', + task: async () => { + const envFiles = [ + '.js.env', + '.ios.env', + '.android.env', + '.e2e.env', + ]; + envFiles.forEach((envFileName) => { + `source ${envFileName}`; + }); + }, + }, + ], + { + concurrent: false, + exitOnError: true, + rendererOptions, + }, + ); }, }; -const ppomBuildTask = { +const buildPpomTask = { title: 'Build PPOM', task: (_, task) => { + if (IS_NODE) { + return task.skip('Skipping building PPOM.'); + } const $ppom = $({ cwd: 'ppom' }); return task.newListr( @@ -79,92 +130,31 @@ const ppomBuildTask = { { concurrent: false, exitOnError: true, + rendererOptions, }, ); }, }; -const gemInstallTask = { - title: 'Install gems', - task: (_, task) => - task.newListr( - [ - { - title: 'Install gems using bundler', - task: async (_, gemInstallTask) => { - if (!BUILD_IOS) { - return gemInstallTask.skip('Skipping iOS.'); - } - await $`bundle install`; - }, - }, - ], - { - exitOnError: true, - concurrent: false, - rendererOptions, - }, - ), -}; - -const mainSetupTask = { - title: 'Dependencies setup', - task: (_, task) => - task.newListr( - [ - { - title: 'Install CocoaPods', - task: async (_, podInstallTask) => { - if (!BUILD_IOS) { - return podInstallTask.skip('Skipping iOS.'); - } - await $`bundle exec pod install --project-directory=ios`; - }, - }, - { - title: 'Run lavamoat allow-scripts', - task: async () => { - await $`yarn allow-scripts`; - }, - }, - copyEnvVarsTask, - ], - { - exitOnError: false, - concurrent: true, - rendererOptions, - }, - ), -}; +const setupIosTask = { + title: 'Set up iOS', + task: async (_, task) => { + if (!BUILD_IOS) { + return task.skip('Skipping iOS set up.'); + } -const patchModulesTask = { - title: 'Patch modules', - task: (_, task) => - task.newListr( + return task.newListr( [ { - title: 'Build Inpage Bridge', - task: async () => { - await $`./scripts/build-inpage-bridge.sh`; - }, - }, - // TODO: find a saner alternative to bring node modules into react native bundler. See ReactNativify - { - title: 'React Native nodeify', + title: 'Install bundler gem', task: async () => { - await $`node_modules/.bin/rn-nodeify --install crypto,buffer,react-native-randombytes,vm,stream,http,https,os,url,net,fs --hack`; + await $`gem install bundler -v 2.5.8`; }, }, { - title: 'Jetify', + title: 'Install gems', task: async () => { - await $`yarn jetify`; - }, - }, - { - title: 'Patch npm packages', - task: async () => { - await $`yarn patch-package`; + await $`yarn gem:bundle:install`; }, }, { @@ -175,9 +165,9 @@ const patchModulesTask = { }, }, { - title: 'Init git submodules', + title: 'Install CocoaPods', task: async () => { - await $`git submodule update --init`; + await $`yarn pod:install`; }, }, ], @@ -185,20 +175,62 @@ const patchModulesTask = { concurrent: false, exitOnError: true, }, - ), + ); + }, }; -const sourceEnvs = { - title: 'Source env vars', +const buildInpageBridgeTask = { + title: 'Build inpage bridge', task: async (_, task) => { - if (IS_CI) { - task.skip('CI detected'); - } else { - const envFiles = ['.js.env', '.ios.env', '.android.env', '.e2e.env']; - envFiles.forEach((envFileName) => { - `source ${envFileName}`; - }); + if (IS_NODE) { + return task.skip('Skipping building inpage bridge.'); + } + await $`./scripts/build-inpage-bridge.sh`; + }, +}; + +const nodeifyTask = { + // TODO: find a saner alternative to bring node modules into react native bundler. See ReactNativify + title: 'Nodeify npm packages', + task: async (_, task) => { + if (IS_NODE) { + return task.skip('Skipping nodeifying npm packages.'); } + await $`node_modules/.bin/rn-nodeify --install crypto,buffer,react-native-randombytes,vm,stream,http,https,os,url,net,fs --hack`; + }, +}; + +const jetifyTask = { + title: 'Jetify npm packages for Android', + task: async (_, task) => { + if (IS_NODE) { + return task.skip('Skipping jetifying npm packages.'); + } + await $`yarn jetify`; + }, +}; + +const patchPackageTask = { + title: 'Patch npm packages', + task: async () => { + await $`yarn patch-package`; + }, +}; + +const updateGitSubmodulesTask = { + title: 'Init git submodules', + task: async (_, task) => { + if (IS_NODE) { + return task.skip('Skipping init git submodules.'); + } + await $`git submodule update --init`; + }, +}; + +const runLavamoatAllowScriptsTask = { + title: 'Run lavamoat allow-scripts', + task: async () => { + await $`yarn allow-scripts`; }, }; @@ -249,32 +281,53 @@ const generateTermsOfUseTask = { { concurrent: false, exitOnError: true, + rendererOptions, }, ), }; -// Optimize for CI performance -const yarnSetupNodeTask = { - title: 'Run yarn setup:node', - task: async () => { - await $`yarn setup:node`; - }, +/** + * Tasks that changes node modules and should run sequentially + */ +const prepareDependenciesTask = { + title: 'Prepare dependencies', + task: (_, task) => + task.newListr( + [ + copyAndSourceEnvVarsTask, + updateGitSubmodulesTask, + // Inpage bridge must generate before node modules are altered + buildInpageBridgeTask, + nodeifyTask, + jetifyTask, + runLavamoatAllowScriptsTask, + patchPackageTask, + ], + { + exitOnError: true, + concurrent: false, + rendererOptions, + }, + ), }; -const taskList = IS_NODE - ? [yarnSetupNodeTask, generateTermsOfUseTask] - : [ - gemInstallTask, - patchModulesTask, - mainSetupTask, - ppomBuildTask, - sourceEnvs, - generateTermsOfUseTask, - ]; +/** + * Tasks that are run concurrently + */ +const concurrentTasks = { + title: 'Concurrent tasks', + task: (_, task) => + task.newListr([setupIosTask, buildPpomTask, generateTermsOfUseTask], { + concurrent: true, + exitOnError: true, + rendererOptions, + }), +}; -const tasks = new Listr(taskList, { - exitOnError: true, +const tasks = new Listr([prepareDependenciesTask, concurrentTasks], { concurrent: false, + exitOnError: true, + rendererOptions, }); await tasks.run();