diff --git a/.github/actions/increment-build-number/action.yml b/.github/actions/increment-build-number/action.yml deleted file mode 100644 index 5e81a47..0000000 --- a/.github/actions/increment-build-number/action.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: 'Increment Build Number' -description: 'Increments the build number or sets it to 1 if not present' -inputs: - gh_token: - description: 'GitHub token for authentication' - required: true - current_build_number: - description: 'The current build number' - required: false - default: '0' -outputs: - build_number: - description: 'The incremented build number' - value: ${{ steps.increment.outputs.build_number }} - -runs: - using: 'composite' - steps: - - name: Increment build number - id: increment - shell: bash - run: | - if [[ -z "${{ inputs.current_build_number }}" || "${{ inputs.current_build_number }}" == "0" ]]; then - build_number=1 - else - build_number=$((${{ inputs.current_build_number }} + 1)) - fi - gh variable set BUILD_NUMBER -b "${build_number}" - echo "build_number=${build_number}" >> $GITHUB_OUTPUT - env: - GH_TOKEN: ${{ inputs.gh_token }} diff --git a/.github/actions/map-environment/action.yml b/.github/actions/map-environment/action.yml new file mode 100644 index 0000000..b5b30c5 --- /dev/null +++ b/.github/actions/map-environment/action.yml @@ -0,0 +1,32 @@ +name: Map Environment +description: 'Maps environment names to Expo environment names' +inputs: + environment: + required: true + description: 'The environment to map (dev, staging) or pass through (development, preview, production)' +outputs: + expo_environment: + description: 'The mapped Expo environment name (development, preview, production)' + value: ${{ steps.map-env.outputs.expo_environment }} +runs: + using: composite + steps: + - name: Map environment to Expo environment + id: map-env + shell: bash + run: | + case "${{ inputs.environment }}" in + "dev") + echo "expo_environment=development" >> $GITHUB_OUTPUT + ;; + "staging") + echo "expo_environment=preview" >> $GITHUB_OUTPUT + ;; + "development"|"preview"|"production") + echo "expo_environment=${{ inputs.environment }}" >> $GITHUB_OUTPUT + ;; + *) + echo "Unknown environment: ${{ inputs.environment }}" + exit 1 + ;; + esac diff --git a/.github/actions/ota-update/action.yml b/.github/actions/ota-update/action.yml index 6bc1c27..563a09d 100644 --- a/.github/actions/ota-update/action.yml +++ b/.github/actions/ota-update/action.yml @@ -7,9 +7,9 @@ inputs: version: required: true description: 'The version number' - build_number: + environment: required: true - description: 'The build number' + description: 'The environment to update' runs: using: composite steps: @@ -17,10 +17,16 @@ runs: shell: bash run: | echo "EXPO_PUBLIC_APP_VERSION=${{ inputs.version }}" >> $GITHUB_ENV - echo "EXPO_PUBLIC_BUILD_NUMBER=${{ inputs.build_number }}" >> $GITHUB_ENV + + - name: Map environment + uses: ./.github/actions/map-environment + id: map-env + with: + environment: ${{ inputs.environment }} - name: Perform OTA Update shell: bash run: | eas update --channel ${{ inputs.channel }} \ - --message "Update ${{ inputs.channel }} to ${{ inputs.version }} (build ${{ inputs.build_number }})" + --environment ${{ steps.map-env.outputs.expo_environment }} \ + --message "Update ${{ inputs.channel }} to ${{ inputs.version }}" diff --git a/.github/actions/update-eas/action.yml b/.github/actions/update-eas/action.yml deleted file mode 100644 index 4ed5373..0000000 --- a/.github/actions/update-eas/action.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: 'Update EAS JSON' -description: 'Updates eas.json with environment-specific variables' -inputs: - environment: - description: 'Environment (e.g. production, staging)' - required: true - version: - description: 'App version' - required: true - build_number: - description: 'Build number' - required: true - vars: - description: 'JSON string of variables' - required: true -runs: - using: 'composite' - steps: - - name: Update eas.json - shell: bash - run: | - jq --arg env "${{ inputs.environment }}" \ - --arg version "${{ inputs.version }}" \ - --arg build "${{ inputs.build_number }}" \ - --argjson vars '${{ inputs.vars }}' \ - '.build[$env].env += ($vars | with_entries(select(.key | startswith("EXPO_PUBLIC_")))) - | .build[$env].env += { - "EXPO_PUBLIC_APP_ENV": $env, - "EXPO_PUBLIC_BUILD_NUMBER": $build, - "EXPO_PUBLIC_APP_VERSION": $version - }' eas.json > eas.json.tmp && mv eas.json.tmp eas.json - echo "Updated eas.json:" diff --git a/.github/workflows/create-dev-build.yml b/.github/workflows/create-dev-build.yml index 3ec31cd..2fb6866 100644 --- a/.github/workflows/create-dev-build.yml +++ b/.github/workflows/create-dev-build.yml @@ -43,22 +43,9 @@ jobs: version_bump: 'none' increment: false - - name: Update eas.json - uses: ./.github/actions/update-eas - with: - environment: dev - version: ${{ steps.version.outputs.version }} - build_number: ${{ vars.BUILD_NUMBER }} - vars: ${{ toJson(vars) }} - - name: Build Application uses: ./.github/actions/build with: environment: ${{ github.event.inputs.environment }} platform: ${{ github.event.inputs.platform }} auto_submit: false - - - name: Revert eas.json changes - if: always() - run: | - git restore eas.json diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index eea6416..12c738d 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -64,21 +64,6 @@ jobs: version_bump: ${{ github.event.inputs.version_bump }} increment: ${{ github.event.inputs.action_type != 'build' }} - - name: Increment build number and save it in env variable - uses: ./.github/actions/increment-build-number - id: build_number - with: - gh_token: ${{ secrets.GT_PAT }} - current_build_number: ${{ vars.BUILD_NUMBER }} - - - name: Update eas.json - uses: ./.github/actions/update-eas - with: - environment: ${{ github.event.inputs.environment }} - version: ${{ steps.version.outputs.version }} - build_number: ${{ steps.build_number.outputs.build_number }} - vars: ${{ toJson(vars) }} - - name: Build Application if: github.event.inputs.action_type != 'ota_update' uses: ./.github/actions/build @@ -90,16 +75,12 @@ jobs: - name: OTA Release if: github.event.inputs.action_type == 'ota_update' uses: ./.github/actions/ota-update - env: ${{ vars }} + env: + VARS: ${{ toJson(vars) }} with: channel: ${{ github.event.inputs.environment }} + environment: ${{ github.event.inputs.environment }} version: ${{ steps.version.outputs.version }} - build_number: ${{ steps.build_number.outputs.build_number }} - - - name: Revert eas.json changes - if: always() - run: | - git restore eas.json - name: Generate Release Notes if: github.event.inputs.action_type != 'build' && github.event.inputs.version_bump != 'none' diff --git a/.github/workflows/production-hotfix-release.yml b/.github/workflows/production-hotfix-release.yml index bc93ff0..738d63c 100644 --- a/.github/workflows/production-hotfix-release.yml +++ b/.github/workflows/production-hotfix-release.yml @@ -40,21 +40,6 @@ jobs: version_bump: 'none' increment: false - - name: Increment build number and save it in env variable - uses: ./.github/actions/increment-build-number - id: build_number - with: - gh_token: ${{ secrets.GT_PAT }} - current_build_number: ${{ vars.BUILD_NUMBER }} - - - name: Update eas.json - uses: ./.github/actions/update-eas - with: - environment: production - version: ${{ steps.version.outputs.version }} - build_number: ${{ steps.build_number.outputs.build_number }} - vars: ${{ toJson(vars) }} - - name: Build and Submit if: github.event.inputs.action_type == 'build_and_submit' uses: ./.github/actions/build @@ -66,16 +51,11 @@ jobs: - name: OTA Update if: github.event.inputs.action_type == 'ota_update' uses: ./.github/actions/ota-update - env: ${{ vars }} + env: + VARS: ${{ toJson(vars) }} with: channel: production version: ${{ steps.version.outputs.version }} - build_number: ${{ steps.build_number.outputs.build_number }} - - - name: Revert eas.json changes - if: always() - run: | - git restore eas.json - name: Create hotfix tag uses: ./.github/actions/create-hotfix-tag diff --git a/.github/workflows/production-submit.yml b/.github/workflows/production-submit.yml index e9eea78..267e73b 100644 --- a/.github/workflows/production-submit.yml +++ b/.github/workflows/production-submit.yml @@ -34,33 +34,12 @@ jobs: version_bump: 'none' increment: false - - name: Increment build number and save it in env variable - uses: ./.github/actions/increment-build-number - id: build_number - with: - gh_token: ${{ secrets.GT_PAT }} - current_build_number: ${{ vars.BUILD_NUMBER }} - - - name: Update eas.json - uses: ./.github/actions/update-eas - with: - environment: production - version: ${{ steps.version.outputs.version }} - build_number: ${{ steps.build_number.outputs.build_number }} - vars: ${{ toJson(vars) }} - - name: Build and Submit uses: ./.github/actions/build with: environment: production platform: ${{ github.event.inputs.platform }} auto_submit: true - - - name: Revert eas.json changes - if: always() - run: | - git restore eas.json - # Uncomment and add your slack webhook url to send Slack notification after the build is submitted # - name: Send Slack Notification # id: slack diff --git a/.gitignore b/.gitignore index 922d820..71057c7 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ web-build/ # env .env +.env.local # native folers android/ diff --git a/README.md b/README.md index 3ba6c71..2fb0800 100644 --- a/README.md +++ b/README.md @@ -193,22 +193,30 @@ To replicate Figma design consistently on majority of mobile screen sizes, we sh ## Adding new `ENV` variables: -**Format**: +We highly recommend using EAS to manage environments. -- All variables must use the `EXPO_PUBLIC` prefix! e.g., `EXPO_PUBLIC_API_URL` +We map our environment names to these Expo environment names. -**Github**: +``` +"dev": "development", +"staging": "preview", +"production": "production" +``` -- There are two options for adding/managing env credentials: +Inside Github Actions we use an action to map the environments automatically. +To add new environment variable: -1. **Variables by a specific env**: +- Either add it through the Expo dashboard +- Or through eas cli: -> Settings -> Environments -> Select/add Env (dev, staging, production) -> Add a new variable +``` +eas env:create --name --value +``` -2. **Shared variables**: +> ⚠️ All variables must use the `EXPO_PUBLIC` prefix! e.g., `EXPO_PUBLIC_API_URL` -> Settings -> Secrets and Variables -> Actions -> Select Variables tab -> New repository variable +You can also pull variables from EAS to your local `.env` file: ``` - +eas env:pull --path ``` diff --git a/app.config.ts b/app.config.ts index e5d031c..a09ccef 100644 --- a/app.config.ts +++ b/app.config.ts @@ -20,6 +20,7 @@ declare const process: { // expoProjectId: '46e1c780-9495-4650-93c8-7f465bf4e1d0', // expoProjectOwner: 'strv-internal', // appScheme: 'template-react-native-expo', +// otaUpdateUrl: 'https://u.expo.dev/46e1c780-9495-4650-93c8-7f465bf4e1d0', // } // Your project defaults @@ -28,6 +29,7 @@ const config = { expoProjectId: undefined, expoProjectOwner: undefined, appScheme: '', + otaUpdateUrl: '', } const environment = process.env.EXPO_PUBLIC_APP_ENV || 'dev' @@ -69,9 +71,8 @@ const plugins: ExpoConfig['plugins'] = [ ['expo-router'], ] -// UPDATE VERSION AND BUILDNUMBER +// UPDATE VERSION const version = process.env.EXPO_PUBLIC_APP_VERSION || packageJson.version -const buildNumber = process.env.EXPO_PUBLIC_BUILD_NUMBER || '1' const fallbackToCacheTimeout = 0 const otaUpdatePriority: OtaUpdatePriority = 'normal' @@ -96,10 +97,10 @@ const expoConfig: ExpoConfig = { }, updates: { fallbackToCacheTimeout, + url: config.otaUpdateUrl, }, assetBundlePatterns: ['**/*'], ios: { - buildNumber, supportsTablet: false, bundleIdentifier: appIdentifier, config: { @@ -113,7 +114,6 @@ const expoConfig: ExpoConfig = { bundler: 'metro', }, android: { - versionCode: parseInt(buildNumber, 10), adaptiveIcon: { foregroundImage: './assets/adaptive-icon.png', backgroundColor: '#FFFFFF', diff --git a/eas.json b/eas.json index 4dd418b..d5e7fc9 100644 --- a/eas.json +++ b/eas.json @@ -1,6 +1,7 @@ { "cli": { - "version": ">= 3.13.3" + "version": ">= 3.13.3", + "appVersionSource": "remote" }, "build": { "base": { @@ -12,16 +13,15 @@ "extends": "base", "developmentClient": true, "distribution": "internal", + "environment": "development", "android": { "buildType": "apk" }, - "channel": "dev", - "env": { - "EXPO_PUBLIC_APP_ENV": "dev" - } + "channel": "dev" }, "dev-sim": { "extends": "dev", + "environment": "development", "ios": { "simulator": true } @@ -30,17 +30,15 @@ "extends": "base", "distribution": "store", "channel": "staging", - "env": { - "EXPO_PUBLIC_APP_ENV": "staging" - } + "autoIncrement": true, + "environment": "preview" }, "production": { "extends": "base", "distribution": "store", "channel": "production", - "env": { - "EXPO_PUBLIC_APP_ENV": "production" - } + "autoIncrement": true, + "environment": "production" } }, "submit": {} diff --git a/src/features/dashboard/screens/DashboardScreen/index.tsx b/src/features/dashboard/screens/DashboardScreen/index.tsx index ac73c68..5108d47 100644 --- a/src/features/dashboard/screens/DashboardScreen/index.tsx +++ b/src/features/dashboard/screens/DashboardScreen/index.tsx @@ -6,7 +6,6 @@ import { styles } from './styles' export const DashboardScreen = () => { return ( - Dashboard ) diff --git a/src/features/versionCheck/components/ForcedUpdate.tsx b/src/features/versionCheck/components/ForcedUpdate.tsx index 77f4511..4126a8b 100644 --- a/src/features/versionCheck/components/ForcedUpdate.tsx +++ b/src/features/versionCheck/components/ForcedUpdate.tsx @@ -4,7 +4,13 @@ import { View, Alert, AlertButton } from 'react-native' import { getStoreLink } from '~/utils/getStoreLink' -export const ForcedUpdate = ({ cancelable = true }: { cancelable?: boolean }) => { +export const ForcedUpdate = ({ + cancelable = true, + onCancel, +}: { + cancelable?: boolean + onCancel?: () => void +}) => { useEffect(() => { const handleUpdate = async () => { const storeLink = getStoreLink() @@ -18,7 +24,7 @@ export const ForcedUpdate = ({ cancelable = true }: { cancelable?: boolean }) => const buttonCancel = { text: 'Not now', - onPress: () => {}, + onPress: onCancel, } const buttonUpdate = { @@ -32,7 +38,7 @@ export const ForcedUpdate = ({ cancelable = true }: { cancelable?: boolean }) => [cancelable && buttonCancel, buttonUpdate].filter(Boolean) as AlertButton[], { cancelable }, ) - }, [cancelable]) + }, [cancelable, onCancel]) return } diff --git a/src/features/versionCheck/hooks/useStoreUpdate/index.tsx b/src/features/versionCheck/hooks/useStoreUpdate/index.tsx index 3d2aacb..c1354d3 100644 --- a/src/features/versionCheck/hooks/useStoreUpdate/index.tsx +++ b/src/features/versionCheck/hooks/useStoreUpdate/index.tsx @@ -1,4 +1,5 @@ import packageJson from 'package.json' +import { useCallback } from 'react' import { Platform } from 'react-native' import semver from 'semver' @@ -21,7 +22,7 @@ type UseStoreUpdateProps = { } export const useStoreUpdate = ({ data, loading }: UseStoreUpdateProps) => { - const [recommendedUpdate] = useStorageString(StorageKeys.RecommendedUpdate) + const [recommendedUpdate, setRecommendedUpdate] = useStorageString(StorageKeys.RecommendedUpdate) const recommendedVersion = Platform.select({ ios: data?.recommendedIOSVersion, @@ -35,6 +36,10 @@ export const useStoreUpdate = ({ data, loading }: UseStoreUpdateProps) => { const shouldForceUpdate = !getIfVersionIsSupported(forceUpdateVersion) + const markRecommendedUpdate = useCallback(() => { + void setRecommendedUpdate(recommendedVersion) + }, [recommendedVersion, setRecommendedUpdate]) + const shouldRecommendUpdate = shouldForceUpdate || recommendedUpdate === recommendedVersion ? false @@ -46,5 +51,6 @@ export const useStoreUpdate = ({ data, loading }: UseStoreUpdateProps) => { shouldForceUpdate, recommendedVersion, forceUpdateVersion, + markRecommendedUpdate, } } diff --git a/src/provider/index.tsx b/src/provider/index.tsx index 18a60e9..f7e3a09 100644 --- a/src/provider/index.tsx +++ b/src/provider/index.tsx @@ -1,6 +1,6 @@ import { useMMKVDevTools } from '@dev-plugins/react-native-mmkv' import * as SplashScreen from 'expo-splash-screen' -import React, { PropsWithChildren, useEffect } from 'react' +import React, { PropsWithChildren, useCallback, useEffect } from 'react' import { GestureHandlerRootView } from 'react-native-gesture-handler' import { OfflineMessage } from '~/features/offlineCheck/components/OfflineMessage' @@ -15,7 +15,7 @@ void SplashScreen.preventAutoHideAsync() setFontScaling() export const Provider = ({ children }: PropsWithChildren) => { - const { shouldForceUpdate, shouldRecommendUpdate } = useStoreUpdate({ + const { shouldForceUpdate, shouldRecommendUpdate, markRecommendedUpdate } = useStoreUpdate({ data: { recommendedIOSVersion: '1.0.0', recommendedAndroidVersion: '1.0.0', @@ -35,11 +35,16 @@ export const Provider = ({ children }: PropsWithChildren) => { } }, [shouldForceUpdate, shouldRecommendUpdate]) + const onCancel = useCallback(() => { + void SplashScreen.hideAsync() + markRecommendedUpdate() + }, [markRecommendedUpdate]) + return ( {children} {shouldForceUpdate && } - {shouldRecommendUpdate && } + {shouldRecommendUpdate && } {isOnline === false && } )