Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion .github/workflows/build-android-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ on:
apk-uploaded:
description: 'Whether the APK was successfully uploaded'
value: ${{ jobs.build-android-apks.outputs.apk-uploaded }}
aab-uploaded:
description: 'Whether the AAB was successfully uploaded'
value: ${{ jobs.build-android-apks.outputs.aab-uploaded }}
inputs:
build_type:
description: 'The type of build to perform'
Expand All @@ -29,15 +32,17 @@ on:
jobs:
build-android-apks:
name: Build Android E2E APKs
runs-on: ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-lg # lg runner: 16 vCPUs, 48GB RAM
runs-on: ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-xl # Bumped from lg to xl to prevent Daemon disappearance issue (Daemon OOM issue in CI)
timeout-minutes: 40
env:
GRADLE_USER_HOME: /home/admin/_work/.gradle
CACHE_GENERATION: v1 # Increment this to bust the cache (v1, v2, v3, etc.)
outputs:
apk-uploaded: ${{ steps.upload-apk.outcome == 'success' }}
aab-uploaded: ${{ steps.upload-aab.outcome == 'success' }}
apk-target-path: ${{ steps.determine-target-paths.outputs.apk-target-path }}
test-apk-target-path: ${{ steps.determine-target-paths.outputs.test-apk-target-path }}
aab-target-path: ${{ steps.determine-target-paths.outputs.aab-target-path }}
artifact_name: ${{ steps.determine-target-paths.outputs.artifact_name }}

steps:
Expand Down Expand Up @@ -83,12 +88,14 @@ jobs:
{
echo "apk-target-path=android/app/build/outputs/apk/flask/release"
echo "test-apk-target-path=android/app/build/outputs/apk/androidTest/flask/release"
echo "aab-target-path=android/app/build/outputs/bundle/flaskRelease"
echo "artifact_name=app-flask-release"
} >> "$GITHUB_OUTPUT"
elif [[ "${{ inputs.build_type }}" == "main" ]]; then
{
echo "apk-target-path=android/app/build/outputs/apk/prod/release"
echo "test-apk-target-path=android/app/build/outputs/apk/androidTest/prod/release"
echo "aab-target-path=android/app/build/outputs/bundle/prodRelease"
echo "artifact_name=app-prod-release"
} >> "$GITHUB_OUTPUT"
else
Expand All @@ -103,6 +110,7 @@ jobs:
path: |
${{ steps.determine-target-paths.outputs.apk-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}.apk
${{ steps.determine-target-paths.outputs.test-apk-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}-androidTest.apk
${{ steps.determine-target-paths.outputs.aab-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}.aab
# Include Gradle properties in key to force rebuild when properties change
# Keep the `hashFiles` call for Gradle config in-sync with these steps:
# - "Cache Gradle dependencies"
Expand Down Expand Up @@ -233,6 +241,7 @@ jobs:
path: |
${{ steps.determine-target-paths.outputs.apk-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}.apk
${{ steps.determine-target-paths.outputs.test-apk-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}-androidTest.apk
${{ steps.determine-target-paths.outputs.aab-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}.aab
# Keep the `hashFiles` call for Gradle config in-sync with these steps:
# - "Check and restore cached APKs if Fingerprint is found"
# - "Cache Gradle dependencies"
Expand All @@ -255,3 +264,13 @@ jobs:
path: ${{ steps.determine-target-paths.outputs.test-apk-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}-androidTest.apk
retention-days: 7
if-no-files-found: error

- name: Upload Android AAB
id: upload-aab
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.build_type }}-${{ inputs.metamask_environment }}-release.aab
path: ${{ steps.determine-target-paths.outputs.aab-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}.aab
retention-days: 7
if-no-files-found: warn
continue-on-error: true
44 changes: 44 additions & 0 deletions .github/workflows/merge-previous-release-branches.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Merge Previous Release Branches

permissions:
pull-requests: write
contents: write
issues: write

on:
create:
# Trigger when a branch is created, filter for release/* happens in the job

jobs:
validate-branch:
name: Validate release branch format
runs-on: ubuntu-latest
# Only run for branch creation (not tags)
if: github.event.ref_type == 'branch'
outputs:
is-valid: ${{ steps.check.outputs.is-valid }}
steps:
- name: Check branch name format
id: check
env:
BRANCH: ${{ github.event.ref }}
run: |
if [[ "$BRANCH" =~ ^release/[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Branch '$BRANCH' matches release/X.Y.Z format"
echo "is-valid=true" >> "$GITHUB_OUTPUT"
else
echo "Branch '$BRANCH' does not match release/X.Y.Z format. Skipping."
echo "is-valid=false" >> "$GITHUB_OUTPUT"
fi

merge-previous-releases:
name: Merge previous release branches
needs: validate-branch
if: needs.validate-branch.outputs.is-valid == 'true'
runs-on: ubuntu-latest
steps:
- name: Merge previous releases
uses: metamask/github-tools/.github/actions/merge-previous-releases@v1.2.0
with:
new-release-branch: ${{ github.event.ref }}
github-token: ${{ secrets.METAMASK_MOBILE_BRANCH_SYNC_TOKEN }}
44 changes: 44 additions & 0 deletions .github/workflows/release-branch-sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Release Branch Sync

permissions:
pull-requests: write
contents: write

on:
pull_request:
types: [closed]
branches:
- stable

jobs:
validate-branch:
name: Validate release branch format
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true
outputs:
is-valid: ${{ steps.check.outputs.is-valid }}
steps:
- name: Check branch name format
id: check
env:
BRANCH: ${{ github.event.pull_request.head.ref }}
run: |
if [[ "$BRANCH" =~ ^release/[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Branch '$BRANCH' matches release/X.Y.Z format"
echo "is-valid=true" >> "$GITHUB_OUTPUT"
else
echo "Branch '$BRANCH' does not match release/X.Y.Z format. Skipping."
echo "is-valid=false" >> "$GITHUB_OUTPUT"
fi

sync-release-branches:
name: Sync open release branches with stable
needs: validate-branch
if: needs.validate-branch.outputs.is-valid == 'true'
runs-on: ubuntu-latest
steps:
- name: Sync release branches with stable
uses: metamask/github-tools/.github/actions/release-branch-sync@v1.2.0
with:
merged-release-branch: ${{ github.event.pull_request.head.ref }}
github-token: ${{ secrets.STABLE_SYNC_TOKEN }}
4 changes: 4 additions & 0 deletions .github/workflows/run-e2e-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ jobs:
outputs:
apk-target-path: ${{ steps.determine-target-paths.outputs.apk-target-path }}
test-apk-target-path: ${{ steps.determine-target-paths.outputs.test-apk-target-path }}
aab-target-path: ${{ steps.determine-target-paths.outputs.aab-target-path }}

env:
PREBUILT_IOS_APP_PATH: artifacts/MetaMask.app
Expand Down Expand Up @@ -130,12 +131,14 @@ jobs:
{
echo "apk-target-path=android/app/build/outputs/apk/flask/release"
echo "test-apk-target-path=android/app/build/outputs/apk/androidTest/flask/release"
echo "aab-target-path=android/app/build/outputs/bundle/flaskRelease"
echo "artifact_name=app-flask-release"
} >> "$GITHUB_OUTPUT"
elif [[ "${{ inputs.build_type }}" == "main" ]]; then
{
echo "apk-target-path=android/app/build/outputs/apk/prod/release"
echo "test-apk-target-path=android/app/build/outputs/apk/androidTest/prod/release"
echo "aab-target-path=android/app/build/outputs/bundle/prodRelease"
echo "artifact_name=app-prod-release"
} >> "$GITHUB_OUTPUT"
else
Expand All @@ -149,6 +152,7 @@ jobs:
echo "🏗 Setting up Android artifacts from build job..."
mkdir -p ${{ steps.determine-target-paths.outputs.apk-target-path }}
mkdir -p ${{ steps.determine-target-paths.outputs.test-apk-target-path }}
mkdir -p ${{ steps.determine-target-paths.outputs.aab-target-path }}

- name: Download Android build artifacts
if: ${{ inputs.platform == 'android' }}
Expand Down
14 changes: 7 additions & 7 deletions android/gradle.properties.github
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# GitHub Actions-specific Gradle settings
# Optimized for E2E builds on GitHub Actions runners

# JVM configuration - tuned for 48GB runner to avoid OOM while maintaining performance
# Heap: 12GB to leave room for Node.js/Metro and native memory
# ExitOnOutOfMemoryError: fail-fast on OOM for CI
org.gradle.jvmargs=-Xmx12g -Xms4g -XX:MaxMetaspaceSize=1g -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:+UseStringDeduplication -XX:MaxGCPauseMillis=500 -XX:+ExitOnOutOfMemoryError -Dfile.encoding=UTF-8
# JVM configuration - balanced settings to avoid OOM while maintaining performance
# Using 16GB heap to leave room for parallel workers and native memory
org.gradle.jvmargs=-Xmx16g -XX:MaxMetaspaceSize=1g -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:+UseStringDeduplication -XX:+OptimizeStringConcat

# Enable performance optimizations but limit parallelism to prevent OOM
org.gradle.parallel=true
org.gradle.configureondemand=true
org.gradle.caching=true
org.gradle.daemon=false
org.gradle.workers.max=2
org.gradle.daemon=true
org.gradle.workers.max=6
org.gradle.vfs.watch=false

# CI-specific optimizations - enabled for GitHub Actions
Expand Down Expand Up @@ -54,4 +54,4 @@ hermesEnabled=true
android.disableResourceValidation=true

# Use legacy packaging to compress native libraries in the resulting APK.
expo.useLegacyPackaging=false
expo.useLegacyPackaging=false
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const styleSheet = (params: {
cellBase: Object.assign(
{
flexDirection: 'row',
alignItems: 'center',
} as ViewStyle,
style,
) as ViewStyle,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ exports[`CellSelectWithMenu should render with default settings correctly 1`] =
<View
style={
{
"alignItems": "center",
"flexDirection": "row",
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,7 @@ exports[`Cell should render CellMultiSelectWithMenu given the type MultiSelectWi
<View
style={
{
"alignItems": "center",
"flexDirection": "row",
}
}
Expand Down Expand Up @@ -1225,6 +1226,7 @@ exports[`Cell should render CellSelectWithMenu given the type SelectWithMenu 1`]
<View
style={
{
"alignItems": "center",
"flexDirection": "row",
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,7 @@ describe('TabBar', () => {
);

fireEvent.press(getByTestId(`tab-bar-item-${TabBarIconKey.Trending}`));
expect(navigation.reset).toHaveBeenCalledWith({
index: 0,
routes: [{ name: Routes.TRENDING_VIEW }],
});
expect(navigation.navigate).toHaveBeenCalledWith(Routes.TRENDING_VIEW);
});

it('does not navigate to trending when trending feature flag is disabled', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,7 @@ const TabBar = ({ state, descriptors, navigation }: TabBarProps) => {
break;
case Routes.TRENDING_VIEW:
if (isAssetsTrendingTokensEnabled) {
navigation.reset({
index: 0,
routes: [{ name: Routes.TRENDING_VIEW }],
});
navigation.navigate(Routes.TRENDING_VIEW);
}
break;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
import type { Theme } from '@metamask/design-tokens';
import { StyleSheet, TextStyle } from 'react-native';

const styleSheet = (params: { theme: Theme }) => {
const { theme } = params;
const { colors } = theme;
return StyleSheet.create({
const styleSheet = () =>
StyleSheet.create({
tokenDetailsContainer: {
marginTop: 16,
gap: 24,
},
contentWrapper: {
paddingVertical: 4,
},
title: {
paddingVertical: 8,
} as TextStyle,
icon: { marginLeft: 4 },
listWrapper: {
paddingTop: 8,
paddingBottom: 8,
Expand All @@ -36,15 +29,6 @@ const styleSheet = (params: { theme: Theme }) => {
lastChild: {
paddingBottom: 0,
},
copyButton: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.primary.muted,
borderRadius: 20,
paddingHorizontal: 8,
marginLeft: 8,
},
});
};

export default styleSheet;
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import React from 'react';
import { TouchableOpacity, View } from 'react-native';
import { useDispatch } from 'react-redux';
import { useTailwind } from '@metamask/design-system-twrnc-preset';
import { Icon, IconName, IconSize } from '@metamask/design-system-react-native';
import { showAlert } from '../../../../../actions/alert';
import { strings } from '../../../../../../locales/i18n';
import { useStyles } from '../../../../../component-library/hooks';
import Text, {
TextColor,
TextVariant,
} from '../../../../../component-library/components/Texts/Text';
import styleSheet from '../TokenDetails.styles';
import Icon, {
IconColor,
IconName,
IconSize,
} from '../../../../../component-library/components/Icons/Icon';
import ClipboardManager from '../../../../../core/ClipboardManager';
import { TokenDetails } from '../TokenDetails';
import TokenDetailsListItem from '../TokenDetailsListItem';
Expand All @@ -26,7 +22,8 @@ interface TokenDetailsListProps {
const TokenDetailsList: React.FC<TokenDetailsListProps> = ({
tokenDetails,
}) => {
const { styles } = useStyles(styleSheet, {});
const tw = useTailwind();
const { styles } = useStyles(styleSheet);
const dispatch = useDispatch();

const handleShowAlert = (config: {
Expand All @@ -49,7 +46,7 @@ const TokenDetailsList: React.FC<TokenDetailsListProps> = ({

return (
<View>
<Text variant={TextVariant.HeadingMD} style={styles.title}>
<Text variant={TextVariant.HeadingMD} style={tw`py-2`}>
{strings('token.token_details')}
</Text>
<View style={styles.listWrapper}>
Expand All @@ -59,18 +56,13 @@ const TokenDetailsList: React.FC<TokenDetailsListProps> = ({
style={[styles.listItem, styles.firstChild]}
>
<TouchableOpacity
style={styles.copyButton}
style={tw`flex-row items-center gap-1`}
onPress={copyAccountToClipboard}
>
<Text color={TextColor.Primary} variant={TextVariant.BodySM}>
<Text variant={TextVariant.BodySM}>
{formatAddress(tokenDetails.contractAddress, 'short')}
</Text>
<Icon
name={IconName.Copy}
size={IconSize.Sm}
color={IconColor.Primary}
style={styles.icon}
/>
<Icon name={IconName.Copy} size={IconSize.Sm} />
</TouchableOpacity>
</TokenDetailsListItem>
)}
Expand Down
Loading
Loading