diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fa4a6a19e86c..0cafedcfad6a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -44,6 +44,7 @@ app/core/Engine/controllers/remote-feature-flag-controller/ @MetaMask/mobile-pla app/core/DeeplinkManager @MetaMask/mobile-platform scripts/build.sh @MetaMask/mobile-platform fingerprint.config.js @MetaMask/mobile-platform +.github/workflows/push-eas-update.yml @MetaMask/mobile-admins scripts/update-expo-channel.js @MetaMask/mobile-admins certs/certificate.pem @MetaMask/mobile-admins ios/fastlane/ @MetaMask/mobile-admins diff --git a/.github/actions/restore-node-modules-permissions/action.yml b/.github/actions/restore-node-modules-permissions/action.yml new file mode 100644 index 000000000000..beb685da2c7c --- /dev/null +++ b/.github/actions/restore-node-modules-permissions/action.yml @@ -0,0 +1,22 @@ +name: 'Restore Node Modules Executable Permissions' +description: 'Restores executable permissions for node_modules binaries after artifact download' + +inputs: + working-directory: + description: 'Working directory where node_modules is located' + required: false + default: '.' + +runs: + using: 'composite' + steps: + - name: Restore executable permissions + shell: bash + working-directory: ${{ inputs.working-directory }} + run: | + echo "🔧 Restoring executable permissions..." + find node_modules/.bin -type f -exec chmod +x {} \; 2>/dev/null || true + find node_modules -type f -name "*.node" -exec chmod +x {} \; 2>/dev/null || true + find node_modules -path "*/bin/*" -type f -exec chmod +x {} \; 2>/dev/null || true + find node_modules -path "*/sdks/*" -type f -exec chmod +x {} \; 2>/dev/null || true + echo "✅ Permissions restored" diff --git a/.github/actions/validate-artifact-compatibility/action.yml b/.github/actions/validate-artifact-compatibility/action.yml new file mode 100644 index 000000000000..5bb8a1d459e8 --- /dev/null +++ b/.github/actions/validate-artifact-compatibility/action.yml @@ -0,0 +1,38 @@ +name: 'Validate Artifact Compatibility' +description: 'Validates that the artifact was built with compatible Node version and OS' + +inputs: + artifact-name: + description: 'The actual artifact name to validate' + required: true + artifact-prefix: + description: 'The expected artifact prefix (e.g., node-modules-eas-update-pr or node-modules-eas-update-base)' + required: true + validation-context: + description: 'Description of what is being validated (e.g., "PR commit", "base branch")' + required: false + default: 'artifact' + +runs: + using: 'composite' + steps: + - name: Validate artifact compatibility + shell: bash + run: | + NODE_VERSION=$(node --version | sed 's/v//') + OS_NAME=$(echo "$RUNNER_OS" | tr '[:upper:]' '[:lower:]') + EXPECTED_ARTIFACT="${{ inputs.artifact-prefix }}-node${NODE_VERSION}-${OS_NAME}" + + echo "🔍 Validating ${{ inputs.validation-context }} artifact compatibility..." + echo " Expected artifact: $EXPECTED_ARTIFACT" + echo " Actual artifact: ${{ inputs.artifact-name }}" + + if [ "$EXPECTED_ARTIFACT" != "${{ inputs.artifact-name }}" ]; then + echo "::error title=Artifact Incompatibility::Node version or OS mismatch detected!" + echo "❌ The node_modules artifact was built with different Node version or OS" + echo " This could cause issues with native node modules" + echo " Expected: $EXPECTED_ARTIFACT" + echo " Actual: ${{ inputs.artifact-name }}" + exit 1 + fi + echo "✅ ${{ inputs.validation-context }} artifact compatibility validated" diff --git a/.github/workflows/push-eas-update.yml b/.github/workflows/push-eas-update.yml index 347701d55276..6d5aa7156730 100644 --- a/.github/workflows/push-eas-update.yml +++ b/.github/workflows/push-eas-update.yml @@ -12,7 +12,7 @@ on: required: true type: string base_branch: - description: 'Base branch ref to compare fingerprints against (e.g., main)' + description: 'Base ref to compare fingerprints against (branch name like "main" or tag name like "v7.61.6")' required: true type: string message: @@ -41,13 +41,43 @@ env: TARGET_CHANNEL: ${{ inputs.channel }} jobs: + setup-dependencies: + name: Setup Dependencies (PR) + needs: + - validate-pr + uses: ./.github/workflows/setup-node-modules.yml + with: + ref: ${{ inputs.commit_hash }} + fetch-depth: 0 + upload-artifact: true + artifact-name: node-modules-eas-update-pr + artifact-retention-days: 1 + + setup-dependencies-base: + name: Setup Dependencies (Base) + needs: + - validate-pr + uses: ./.github/workflows/setup-node-modules.yml + with: + ref: ${{ inputs.base_branch }} + fetch-depth: 0 + upload-artifact: true + artifact-name: node-modules-eas-update-base + artifact-retention-days: 1 + fingerprint-comparison: name: Compare Expo Fingerprints + needs: + - setup-dependencies + - setup-dependencies-base runs-on: ubuntu-latest outputs: branch_fingerprint: ${{ steps.branch_fingerprint.outputs.fingerprint }} main_fingerprint: ${{ steps.main_fingerprint.outputs.fingerprint }} fingerprints_equal: ${{ steps.compare.outputs.equal }} + env: + PR_ARTIFACT_NAME: ${{ needs.setup-dependencies.outputs.artifact-name }} + BASE_ARTIFACT_NAME: ${{ needs.setup-dependencies-base.outputs.artifact-name }} steps: - name: Checkout target commit uses: actions/checkout@v4 @@ -67,12 +97,35 @@ jobs: with: node-version: '20' - - name: Install dependencies (workflow branch) + - name: Validate artifact compatibility (PR commit) + uses: ./.github/actions/validate-artifact-compatibility + with: + artifact-name: ${{ env.PR_ARTIFACT_NAME }} + artifact-prefix: node-modules-eas-update-pr + validation-context: PR commit + + - name: Download node_modules artifact (PR commit) + uses: actions/download-artifact@v4 + with: + name: ${{ env.PR_ARTIFACT_NAME }} + + - name: Restore executable permissions + uses: ./.github/actions/restore-node-modules-permissions + + - name: Verify downloaded artifacts run: | - echo "📦 Installing dependencies for current branch..." - yarn install --immutable + echo "✅ Verifying downloaded artifacts..." + if [ ! -d "node_modules" ]; then + echo "❌ node_modules directory not found" + exit 1 + fi + if [ ! -f "app/core/InpageBridgeWeb3.js" ]; then + echo "❌ InpageBridgeWeb3.js not found in artifact" + exit 1 + fi + echo "✅ Artifacts verified" - - name: Generate fingerprint (workflow branch) + - name: Generate fingerprint (target commit) id: branch_fingerprint run: | echo "🧬 Generating fingerprint for current branch..." @@ -80,11 +133,23 @@ jobs: echo "fingerprint=$FINGERPRINT" >> "$GITHUB_OUTPUT" echo "Target PR fingerprint: $FINGERPRINT" - - name: Install dependencies (base branch) - working-directory: main - run: | - echo "📦 Installing dependencies for base branch snapshot (${BASE_BRANCH_REF})..." - yarn install --immutable + - name: Validate artifact compatibility (base branch) + uses: ./.github/actions/validate-artifact-compatibility + with: + artifact-name: ${{ env.BASE_ARTIFACT_NAME }} + artifact-prefix: node-modules-eas-update-base + validation-context: base branch + + - name: Download node_modules artifact (base branch) + uses: actions/download-artifact@v4 + with: + name: ${{ env.BASE_ARTIFACT_NAME }} + path: main + + - name: Restore executable permissions (base branch) + uses: ./.github/actions/restore-node-modules-permissions + with: + working-directory: main - name: Generate fingerprint (base branch) id: main_fingerprint @@ -163,10 +228,10 @@ jobs: if: ${{ needs.fingerprint-comparison.outputs.fingerprints_equal == 'true' }} runs-on: ubuntu-latest steps: - - name: Await approval from mobile platform team + - name: Await approval from mobile release team uses: op5dev/require-team-approval@dfd7b8b9a88bf82a955c103f7e19642b0411aecd with: - team: mobile-platform + team: release-team pr-number: ${{ needs.validate-pr.outputs.pr_number }} token: ${{ secrets.METAMASK_MOBILE_ORG_READ_TOKEN }} @@ -178,10 +243,12 @@ jobs: - fingerprint-comparison - approval - validate-pr + - setup-dependencies if: > needs.fingerprint-comparison.outputs.fingerprints_equal == 'true' && needs.approval.result == 'success' env: + ARTIFACT_NAME: ${{ needs.setup-dependencies.outputs.artifact-name }} EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} EXPO_PROJECT_ID: ${{ secrets.EXPO_PROJECT_ID }} EXPO_CHANNEL: ${{ vars.EXPO_CHANNEL }} @@ -240,6 +307,35 @@ jobs: with: node-version: '20' + - name: Validate artifact compatibility + uses: ./.github/actions/validate-artifact-compatibility + with: + artifact-name: ${{ env.ARTIFACT_NAME }} + artifact-prefix: node-modules-eas-update-pr + validation-context: artifact + + - name: Download node_modules artifact + uses: actions/download-artifact@v4 + with: + name: ${{ env.ARTIFACT_NAME }} + + - name: Restore executable permissions + uses: ./.github/actions/restore-node-modules-permissions + + - name: Verify downloaded artifacts + run: | + echo "✅ Verifying downloaded artifacts..." + if [ ! -d "node_modules" ]; then + echo "❌ node_modules directory not found" + exit 1 + fi + if [ ! -f "app/core/InpageBridgeWeb3.js" ]; then + echo "❌ InpageBridgeWeb3.js not found in artifact" + exit 1 + fi + echo "📦 node_modules size: $(du -sh node_modules | cut -f1)" + echo "✅ Artifacts verified" + - name: Determine signing secret name shell: bash env: @@ -286,16 +382,6 @@ jobs: echo "✅ Set secret for key: $key" done - - name: Install dependencies - run: | - echo "📦 Installing dependencies..." - yarn install --immutable - - - name: Setup project - run: | - echo "🔧 Running setup for GitHub CI..." - yarn setup:github-ci - - name: Display configuration run: | TARGET_RUNTIME_VERSION=$(node -p "require('./package.json').version") diff --git a/.github/workflows/setup-node-modules.yml b/.github/workflows/setup-node-modules.yml new file mode 100644 index 000000000000..32dc7f704673 --- /dev/null +++ b/.github/workflows/setup-node-modules.yml @@ -0,0 +1,123 @@ +# Reusable workflow for setting up node_modules +# This workflow installs dependencies and runs the project setup, then uploads +# the prepared node_modules as an artifact for consumption by other workflows. + +name: Setup Node Modules + +on: + workflow_call: + inputs: + ref: + description: 'Git ref to checkout (e.g., refs/pull/123/head or branch name)' + required: false + type: string + default: '' + fetch-depth: + description: 'Number of commits to fetch (0 for all history)' + required: false + type: number + default: 1 + upload-artifact: + description: 'Whether to upload node_modules as an artifact' + required: false + type: boolean + default: true + artifact-name: + description: 'Name of the artifact to upload' + required: false + type: string + default: 'node-modules' + artifact-retention-days: + description: 'Number of days to retain the artifact' + required: false + type: number + default: 1 + outputs: + artifact-name: + description: 'The actual artifact name used (includes node version and OS)' + value: ${{ jobs.setup.outputs.artifact-name }} + +permissions: + contents: read + +jobs: + setup: + name: Setup Node Modules + runs-on: ubuntu-latest + outputs: + artifact-name: ${{ steps.set-artifact-name.outputs.artifact-name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref }} + fetch-depth: ${{ inputs.fetch-depth }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Set artifact name with node version and OS + id: set-artifact-name + run: | + NODE_VERSION=$(node --version | sed 's/v//') + OS_NAME=$(echo "$RUNNER_OS" | tr '[:upper:]' '[:lower:]') + ARTIFACT_NAME="${{ inputs.artifact-name }}-node${NODE_VERSION}-${OS_NAME}" + echo "artifact-name=$ARTIFACT_NAME" >> "$GITHUB_OUTPUT" + echo "📦 Artifact name: $ARTIFACT_NAME" + + - name: Install dependencies + run: | + echo "📦 Installing dependencies..." + yarn install --immutable + + - name: Setup project + run: | + echo "🔧 Running setup for GitHub CI..." + yarn setup:github-ci + + - name: Verify setup completed + run: | + echo "✅ Verifying setup artifacts..." + # Check that critical setup artifacts exist + if [ ! -d "node_modules" ]; then + echo "❌ node_modules directory not found" + exit 1 + fi + if [ ! -f "app/util/termsOfUse/termsOfUseContent.ts" ]; then + echo "❌ Terms of Use content not generated" + exit 1 + fi + if [ ! -f "app/core/InpageBridgeWeb3.js" ]; then + echo "❌ InpageBridgeWeb3.js not generated" + exit 1 + fi + echo "🔍 Checking yarn setup..." + echo " Node version: $(node --version)" + echo " Yarn version: $(yarn --version)" + echo " Corepack version: $(corepack --version)" + echo "✅ Setup verification passed" + + - name: Debug - List .yarn contents + run: | + echo "📂 Contents of .yarn directory:" + ls -la .yarn/ || echo "No .yarn directory" + echo "📊 .yarn size: $(du -sh .yarn 2>/dev/null || echo 'N/A')" + echo "📄 install-state.gz exists: $(test -f .yarn/install-state.gz && echo 'yes' || echo 'no')" + echo "📁 cache dir exists: $(test -d .yarn/cache && echo 'yes' || echo 'no')" + + - name: Upload node_modules artifact + if: inputs.upload-artifact + uses: actions/upload-artifact@v4.5.0 + with: + name: ${{ steps.set-artifact-name.outputs.artifact-name }} + path: | + node_modules + app/util/termsOfUse/termsOfUseContent.ts + app/core/InpageBridgeWeb3.js + scripts/inpage-bridge/dist + retention-days: ${{ inputs.artifact-retention-days }} + compression-level: 1 + if-no-files-found: warn + include-hidden-files: true