From e339b9750940803c5114d0207be079161a48e417 Mon Sep 17 00:00:00 2001 From: Lucas Raposeiras Date: Fri, 4 Apr 2025 14:20:21 -0300 Subject: [PATCH] feat: EAS build with GitHub Actions --- .github/workflows/eas-build.yml | 165 +++++++++++++++++++++++++------- .gitignore | 5 +- app.config.ts | 3 + eas.json | 59 +++++++++--- package.json | 18 ++-- 5 files changed, 195 insertions(+), 55 deletions(-) diff --git a/.github/workflows/eas-build.yml b/.github/workflows/eas-build.yml index 12db95f8..c3f84ee2 100644 --- a/.github/workflows/eas-build.yml +++ b/.github/workflows/eas-build.yml @@ -6,54 +6,104 @@ # This workflow is used to trigger a build on EAS. # Can be triggered manually from the actions tab. -# This action accepts those inputs: -# `environment`, which is used to generate a build for a specific environment (development, staging, QA, production). We use staging by default. -# `android`, true by default, set to true if you don't want to trigger build for Android. -# `ios`, false by default, set to true if you want to trigger build for IOS. - # Before triggering the build, we run a pre-build script to generate the necessary native folders based on the APP_ENV. # Based on the ANDROID and IOS inputs, we trigger the build for the corresponding platform with the corresponding flags. # 🚨 GITHUB SECRETS REQUIRED: -# - EXPO_TOKEN: Expo token to authenticate with EAS -# - You can get it from https://expo.dev/settings/access-tokens +# - EXPO_TOKEN: Expo token to authenticate with EAS to sync version numbers and submit the build. +# You can get it from https://expo.dev/settings/access-tokens +# - NEW_VERSION_NUMBER_PAT: A fine-grained Personal Access Token. +# This token is used to commit and push to protected branches. +# You can generate one from here: https://github.com/settings/tokens?type=beta +# Set the token name to something meaningful, e.g. "New version number PAT for ". +# Set the Repository access to "Only select repositories" and select this repository. +# Set the following Repo permissions: +# - Contents: Read & write (to commit and push) +# Make sure to add it to the repo secrets with the name NEW_VERSION_NUMBER_PAT: +# - Go to Repository Settings > Secrets and variables > Actions > New repository secret +# - Name: NEW_VERSION_NUMBER_PAT +# - Value: The Personal Access Token you created -name: 'EAS Build (Android & IOS) (EAS)' +name: 'Build & Deploy' on: workflow_dispatch: inputs: - environment: + platform: type: choice - description: 'Environment' - required: true - default: 'staging' + description: Platform to build for options: - - development - - staging - - qa - - production - android: - type: boolean - description: 'Build for Android' - required: true - default: true - ios: + - android + - ios + new-version: + type: string + description: 'New version (e.g. 1.0.0) (optional)' + auto-submit: type: boolean - description: 'Build for iOS' - required: true + description: 'Auto-submit the build to the store' + required: false default: true jobs: - Build: + validate-new-version: runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + if: inputs.new-version + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Install Node.js + if: inputs.new-version + uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Validate new version + if: inputs.new-version + run: | + CURRENT_VERSION=$(node -p "require('./package.json').version") + NEW_VERSION="${{ inputs.new-version }}" + + echo "Current version: $CURRENT_VERSION" + echo "New version: $NEW_VERSION" + + npx semver -r ">=$CURRENT_VERSION" "$NEW_VERSION" > /dev/null || (echo "❌ New version must be greater than or equal to current version ($CURRENT_VERSION)" && exit 1) + + echo "✅ New version is valid" + + build: + needs: validate-new-version + runs-on: ${{ inputs.platform == 'ios' && 'macos-latest' || 'ubuntu-latest' }} + permissions: + contents: write + environment: ${{ github.ref_name == 'main' && 'production' || github.ref_name == 'staging' && 'staging' || github.ref_name == 'qa' && 'qa' || github.ref_name == 'development' && 'development' }} steps: - - name: Check for EXPO_TOKEN + - name: Set environment variable + run: | + if [[ "${{ github.ref_name }}" == "main" ]]; then + echo "ENV=production" >> $GITHUB_ENV + elif [[ "${{ github.ref_name }}" == "staging" ]]; then + echo "ENV=staging" >> $GITHUB_ENV + elif [[ "${{ github.ref_name }}" == "qa" ]]; then + echo "ENV=qa" >> $GITHUB_ENV + elif [[ "${{ github.ref_name }}" == "development" ]]; then + echo "ENV=development" >> $GITHUB_ENV + else + echo "Invalid branch: ${{ github.ref_name }}. You can only build for main, staging, qa or development branches." + exit 1 + fi + - name: Check if all required secrets exist run: | if [ -z "${{ secrets.EXPO_TOKEN }}" ]; then echo "You must provide an EXPO_TOKEN secret linked to this project's Expo account in this repo's secrets. Learn more: https://docs.expo.dev/eas-update/github-actions" exit 1 fi + if [ -z "${{ secrets.NEW_VERSION_NUMBER_PAT }}" ]; then + echo "NEW_VERSION_NUMBER_PAT secret not found. Please create a fine-grained Personal Access Token following the instructions in the workflow file." + exit 1 + fi - name: 📦 Setup Expo and EAS uses: expo/expo-github-action@v8 @@ -65,17 +115,66 @@ jobs: uses: actions/checkout@v3 with: fetch-depth: 0 + token: ${{ secrets.NEW_VERSION_NUMBER_PAT }} - name: 📦 Setup Node + PNPM + install deps uses: ./.github/actions/setup-node-pnpm-install + - name: Create environment file + run: echo "${{ secrets.ENVIRONMENT_FILE }}" > .env.${{ env.ENV }} + + - name: Update version in package.json + if: inputs.new-version + run: | + CURRENT_VERSION=$(node -p "require('./package.json').version") + if [ "$CURRENT_VERSION" == "${{inputs.new-version}}" ]; then + echo "Current version is already ${{ inputs.new-version }}, no need to update" + exit 0 + fi + + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + pnpm version ${{ inputs.new-version }} -m "chore: set app version to ${{ inputs.new-version }}" + + - name: Setup latest version of Xcode + uses: maxim-lobanov/setup-xcode@v1 + if: inputs.platform == 'ios' + with: + xcode-version: latest + + - name: Set Up JDK + uses: actions/setup-java@v3 + if: inputs.platform == 'android' + with: + distribution: 'zulu' # See 'Supported distributions' for available options + java-version: '17' + - name: ⚙️ Run Prebuild - run: pnpm prebuild:${{ inputs.environment }} + run: pnpm prebuild:${{ env.ENV }} - - name: 📱 Run Android Build - if: ${{ inputs.android == true }} - run: pnpm build:${{ inputs.environment }}:android --non-interactive --no-wait --message "Build ${{ inputs.environment }}" + - name: 📱 Run Build for ${{ inputs.platform }} + run: | + pnpm build:${{ env.ENV }}:${{ inputs.platform }} --non-interactive --no-wait --message "Build ${{ env.ENV }} for ${{ inputs.platform }}" --local + if [ "${{ inputs.platform }}" = "android" ]; then + ls -1 ./*.aab > path.txt + elif [ "${{ inputs.platform }}" = "ios" ]; then + ls -1 ./*.ipa > path.txt + fi + - name: Upload ${{ inputs.platform }} Build + uses: actions/upload-artifact@v4 + with: + name: ${{ inputs.platform }}-build-${{ env.ENV }} + path: | + *.apks + *.apk + *.aab + *.ipa + + - name: 📱 Submit ${{ inputs.platform }} app to the store + if: inputs.auto-submit + run: pnpm submit:${{ env.ENV }}:mobile --platform=${{ inputs.platform }} --path=$(cat path.txt) --no-wait --non-interactive - - name: 📱 Run IOS Build - if: ${{ inputs.ios == true }} - run: pnpm build:${{ inputs.environment }}:ios --non-interactive --no-wait --message "Build ${{ inputs.environment }}" + - name: 📦 Push changes to repository + if: inputs.new-version + run: | + git push || echo "Skipping push: version was already updated." \ No newline at end of file diff --git a/.gitignore b/.gitignore index 94dbb839..f8533c50 100644 --- a/.gitignore +++ b/.gitignore @@ -20,9 +20,12 @@ yarn-error.log # macOS .DS_Store + +# artifacts +*.apks *.apk *.ipa - +*.aab # @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb # The following patterns were generated by expo-cli diff --git a/app.config.ts b/app.config.ts index dc3e8729..641855ee 100644 --- a/app.config.ts +++ b/app.config.ts @@ -30,6 +30,9 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ ios: { supportsTablet: true, bundleIdentifier: Env.BUNDLE_ID, + config: { + usesNonExemptEncryption: false, + }, }, experiments: { typedRoutes: true, diff --git a/eas.json b/eas.json index 6dbe7ab9..25bce2cc 100644 --- a/eas.json +++ b/eas.json @@ -1,9 +1,11 @@ { "cli": { - "version": ">= 0.57.0" + "version": ">= 0.57.0", + "appVersionSource": "remote" }, "build": { "production": { + "autoIncrement": true, "channel": "production", "distribution": "store", "pnpm": "8.15.4", @@ -17,7 +19,8 @@ "env": { "EXPO_NO_DOTENV": "1", "APP_ENV": "production", - "FLIPPER_DISABLE": "1" + "FLIPPER_DISABLE": "1", + "EAS_NO_DOCTOR": "1" }, "prebuildCommand": "prebuild --skip-dependency-update react", "cache": { @@ -25,6 +28,7 @@ } }, "staging": { + "autoIncrement": true, "channel": "staging", "distribution": "store", "pnpm": "8.15.4", @@ -32,13 +36,14 @@ "image": "latest" }, "android": { - "buildType": "apk", + "buildType": "app-bundle", "image": "latest" }, "env": { "APP_ENV": "staging", "EXPO_NO_DOTENV": "1", - "FLIPPER_DISABLE": "1" + "FLIPPER_DISABLE": "1", + "EAS_NO_DOCTOR": "1" }, "prebuildCommand": "prebuild --skip-dependency-update react", "cache": { @@ -46,6 +51,7 @@ } }, "qa": { + "autoIncrement": true, "channel": "qa", "distribution": "store", "pnpm": "8.15.4", @@ -53,33 +59,36 @@ "image": "latest" }, "android": { - "buildType": "apk", + "buildType": "app-bundle", "image": "latest" }, "env": { "APP_ENV": "qa", "EXPO_NO_DOTENV": "1", - "FLIPPER_DISABLE": "1" + "FLIPPER_DISABLE": "1", + "EAS_NO_DOCTOR": "1" }, - "prebuildCommand": "prebuild --skip-dependency-update react", "cache": { "key": "eas-1" } }, "development": { + "autoIncrement": true, "developmentClient": true, - "distribution": "internal", + "distribution": "store", "pnpm": "8.15.4", "ios": { "image": "latest" }, "android": { + "buildType": "app-bundle", "image": "latest" }, "env": { "APP_ENV": "development", - "EXPO_NO_DOTENV": "1" + "EXPO_NO_DOTENV": "1", + "EAS_NO_DOCTOR": "1" }, "prebuildCommand": "prebuild --skip-dependency-update react", "cache": { @@ -108,6 +117,34 @@ } }, "submit": { - "production": {} + "production": { + "ios": { + "ascAppId": "6746460022" + } + }, + "staging": { + "ios": { + "ascAppId": "6745524568" + }, + "android": { + "releaseStatus": "draft" + } + }, + "qa": { + "ios": { + "ascAppId": "6745527331" + }, + "android": { + "releaseStatus": "draft" + } + }, + "development": { + "ios": { + "ascAppId": "6746459808" + }, + "android": { + "releaseStatus": "draft" + } + } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index 903e9662..432e734d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-template", - "version": "1.7.0", + "version": "3.0.1", "private": true, "main": "expo-router/entry", "scripts": { @@ -14,8 +14,7 @@ "build:development:android": "cross-env APP_ENV=development EXPO_NO_DOTENV=1 eas build --profile development --platform android ", "build:development:ios": "cross-env APP_ENV=development EXPO_NO_DOTENV=1 eas build --profile development --platform ios", "build:development:web": "cross-env APP_ENV=development EXPO_NO_DOTENV=1 expo export -c --platform web", - "credentials:development:ios": "APP_ENV=development eas credentials:configure-build --profile=development --platform ios", - "submit:development:mobile": "cross-env APP_ENV=development eas submit", + "submit:development:mobile": "cross-env APP_ENV=development eas submit --profile development", "start:qa": "cross-env APP_ENV=qa pnpm run start", "prebuild:qa": "cross-env APP_ENV=qa pnpm run prebuild", "android:qa": "cross-env APP_ENV=qa pnpm run android", @@ -24,8 +23,7 @@ "build:qa:android": "cross-env APP_ENV=qa EXPO_NO_DOTENV=1 eas build --profile qa --platform android ", "build:qa:ios": "cross-env APP_ENV=qa EXPO_NO_DOTENV=1 eas build --profile qa --platform ios", "build:qa:web": "cross-env APP_ENV=qa EXPO_NO_DOTENV=1 expo export -c --platform web", - "credentials:qa:ios": "APP_ENV=qa eas credentials:configure-build --profile=qa --platform ios", - "submit:qa:mobile": "cross-env APP_ENV=qa eas submit", + "submit:qa:mobile": "cross-env APP_ENV=qa eas submit --profile qa", "start:staging": "cross-env APP_ENV=staging pnpm run start", "prebuild:staging": "cross-env APP_ENV=staging pnpm run prebuild", "android:staging": "cross-env APP_ENV=staging pnpm run android", @@ -34,8 +32,7 @@ "build:staging:android": "cross-env APP_ENV=staging EXPO_NO_DOTENV=1 eas build --profile staging --platform android ", "build:staging:ios": "cross-env APP_ENV=staging EXPO_NO_DOTENV=1 eas build --profile staging --platform ios", "build:staging:web": "cross-env APP_ENV=staging EXPO_NO_DOTENV=1 expo export -c --platform web", - "credentials:staging:ios": "APP_ENV=staging eas credentials:configure-build --profile=staging --platform ios", - "submit:staging:mobile": "cross-env APP_ENV=staging eas submit", + "submit:staging:mobile": "cross-env APP_ENV=staging eas submit --profile staging", "start:production": "cross-env APP_ENV=production pnpm run start", "prebuild:production": "cross-env APP_ENV=production pnpm run prebuild", "android:production": "cross-env APP_ENV=production pnpm run android", @@ -44,14 +41,15 @@ "build:production:android": "cross-env APP_ENV=production EXPO_NO_DOTENV=1 eas build --profile production --platform android ", "build:production:ios": "cross-env APP_ENV=production EXPO_NO_DOTENV=1 eas build --profile production --platform ios", "build:production:web": "cross-env APP_ENV=production EXPO_NO_DOTENV=1 expo export -c --platform web", - "credentials:production:ios": "APP_ENV=production eas credentials:configure-build --profile=production --platform ios", - "submit:production:mobile": "cross-env APP_ENV=production eas submit", + "submit:production:mobile": "cross-env APP_ENV=production eas submit --profile production", + "credentials:ios": "eas credentials --platform ios", + "credentials:android": "eas credentials --platform android", "prepare": "husky", "xcode": "xed -b ios", "doctor": "npx expo-doctor@latest", "preinstall": "npx only-allow pnpm", "app-release": "cross-env SKIP_BRANCH_PROTECTION=true np --no-publish --no-cleanup --no-release-draft --message 'chore: release template v%s'", - "version": "git add .", + "version": "git add package.json", "lint": "eslint . --ext .js,.jsx,.ts,.tsx", "type-check": "tsc --noemit", "lint:translations": "eslint ./src/translations/ --fix --ext .json ",