1+ name : React Native CI/CD
2+
3+ on :
4+ push :
5+ branches : [main, master]
6+ paths-ignore :
7+ - " **.md"
8+ - " LICENSE"
9+ - " docs/**"
10+ pull_request :
11+ branches : [main, master]
12+ workflow_dispatch :
13+ inputs :
14+ buildType :
15+ type : choice
16+ description : " Build type to run"
17+ options :
18+ - dev
19+ - prod-apk
20+ - prod-aab
21+ - ios-dev
22+ - ios-prod
23+ - all
24+ platform :
25+ type : choice
26+ description : " Platform to build"
27+ default : " all"
28+ options :
29+ - android
30+ - ios
31+ - all
32+
33+ env :
34+ EXPO_TOKEN : ${{ secrets.EXPO_TOKEN }}
35+ EXPO_APPLE_ID : ${{ secrets.EXPO_APPLE_ID }}
36+ EXPO_APPLE_PASSWORD : ${{ secrets.EXPO_APPLE_PASSWORD }}
37+ EXPO_TEAM_ID : ${{ secrets.EXPO_TEAM_ID }}
38+ NODE_OPTIONS : --openssl-legacy-provider
39+
40+ jobs :
41+ check-skip :
42+ runs-on : ubuntu-latest
43+ if : " !contains(github.event.head_commit.message, '[skip ci]')"
44+ steps :
45+ - name : Skip CI check
46+ run : echo "Proceeding with workflow"
47+
48+ test :
49+ needs : check-skip
50+ runs-on : ubuntu-latest
51+ steps :
52+ - name : 🏗 Checkout repository
53+ uses : actions/checkout@v4
54+
55+ - name : 🏗 Setup Node.js
56+ uses : actions/setup-node@v4
57+ with :
58+ node-version : " 20"
59+ cache : " yarn"
60+
61+ - name : 📦 Get yarn cache directory path
62+ id : yarn-cache-dir-path
63+ run : echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
64+
65+ - name : 📦 Setup yarn cache
66+ uses : actions/cache@v3
67+ with :
68+ path : ${{ steps.yarn-cache-dir-path.outputs.dir }}
69+ key : ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
70+ restore-keys : |
71+ ${{ runner.os }}-yarn-
72+
73+ - name : 📦 Install dependencies
74+ run : yarn install
75+
76+ - name : 🧪 Run TypeScript check
77+ run : yarn tsc
78+
79+ - name : 🧹 Run ESLint
80+ run : yarn lint
81+
82+ - name : 🎨 Run Prettier check
83+ run : yarn format:check
84+
85+ - name : 🧪 Run Jest Tests
86+ run : yarn test
87+
88+ - name : 🧪 Run React Native Testing Library Tests
89+ run : yarn test:rntl
90+
91+ build-and-deploy :
92+ needs : test
93+ if : (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')) || github.event_name == 'workflow_dispatch'
94+ strategy :
95+ matrix :
96+ platform : [android]
97+ include :
98+ - platform : ios
99+ runs-on : macos-latest
100+ runs-on : ${{ matrix.platform == 'ios' && 'macos-latest' || 'ubuntu-latest' }}
101+ steps :
102+ - name : 🏗 Checkout repository
103+ uses : actions/checkout@v4
104+
105+ - name : 🏗 Setup Node.js
106+ uses : actions/setup-node@v4
107+ with :
108+ node-version : " 20"
109+ cache : " yarn"
110+
111+ - name : 📦 Get yarn cache directory path
112+ id : yarn-cache-dir-path
113+ run : echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
114+
115+ - name : 📦 Setup yarn cache
116+ uses : actions/cache@v3
117+ with :
118+ path : ${{ steps.yarn-cache-dir-path.outputs.dir }}
119+ key : ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
120+ restore-keys : |
121+ ${{ runner.os }}-yarn-
122+
123+ - name : 📦 Install dependencies
124+ run : |
125+ yarn install
126+ yarn global add eas-cli@latest
127+
128+ - name : 📱 Setup EAS build cache
129+ uses : actions/cache@v3
130+ with :
131+ path : ~/.eas-build-local
132+ key : ${{ runner.os }}-eas-build-local-${{ hashFiles('**/package.json') }}
133+ restore-keys : |
134+ ${{ runner.os }}-eas-build-local-
135+
136+ - name : 🔄 Verify EAS CLI installation
137+ run : |
138+ echo "EAS CLI version:"
139+ eas --version
140+
141+ - name : 📋 Fix package.json main entry
142+ run : |
143+ # Check if jq is installed, if not install it
144+ if ! command -v jq &> /dev/null; then
145+ echo "Installing jq..."
146+ sudo apt-get update && sudo apt-get install -y jq
147+ fi
148+
149+ # Fix the main entry in package.json
150+ if [ -f ./package.json ]; then
151+ # Create a backup
152+ cp package.json package.json.bak
153+ # Update the package.json
154+ jq '.main = "node_modules/expo/AppEntry.js"' package.json > package.json.tmp && mv package.json.tmp package.json
155+ echo "Updated package.json main entry"
156+ cat package.json | grep "main"
157+ else
158+ echo "package.json not found"
159+ exit 1
160+ fi
161+
162+ - name : 📋 Update metro.config.js for SVG support
163+ run : |
164+ if [ -f ./metro.config.js ]; then
165+ echo "Creating backup of metro.config.js"
166+ cp ./metro.config.js ./metro.config.js.backup
167+ echo "Updating metro.config.js to CommonJS format"
168+ cat > ./metro.config.js << 'EOFMARKER'
169+ /* eslint-disable @typescript-eslint/no-var-requires */
170+ const { getDefaultConfig } = require('expo/metro-config');
171+
172+ const config = getDefaultConfig(__dirname);
173+
174+ const { transformer, resolver } = config;
175+
176+ config.transformer = {
177+ ...transformer,
178+ babelTransformerPath: require.resolve('react-native-svg-transformer/expo'),
179+ };
180+
181+ config.resolver = {
182+ ...resolver,
183+ assetExts: resolver.assetExts.filter(ext => ext !== 'svg'),
184+ sourceExts: [...resolver.sourceExts, 'svg'],
185+ };
186+
187+ module.exports = config;
188+ EOFMARKER
189+ echo "metro.config.js updated to CommonJS format"
190+ else
191+ echo "metro.config.js not found"
192+ fi
193+
194+ - name : 📱 Build Development APK
195+ if : github.event.inputs.buildType == 'all' || github.event.inputs.buildType == 'dev' || github.event_name == 'push' && (matrix.platform == 'android' || github.event.inputs.platform == 'all' || github.event.inputs.platform == 'android')
196+ run : |
197+ # Build with increased memory limit
198+ export NODE_OPTIONS="--openssl-legacy-provider --max_old_space_size=4096"
199+ eas build --platform android --profile development --local --non-interactive --output=./app-dev.apk
200+ env :
201+ NODE_ENV : development
202+
203+ - name : 📱 Build Production APK
204+ if : github.event.inputs.buildType == 'all' || github.event.inputs.buildType == 'prod-apk' || github.event_name == 'push' && (matrix.platform == 'android' || github.event.inputs.platform == 'all' || github.event.inputs.platform == 'android')
205+ run : |
206+ export NODE_OPTIONS="--openssl-legacy-provider --max_old_space_size=4096"
207+ eas build --platform android --profile production-apk --local --non-interactive --output=./app-prod.apk
208+ env :
209+ NODE_ENV : production
210+
211+ - name : 📱 Build Production AAB
212+ if : github.event.inputs.buildType == 'all' || github.event.inputs.buildType == 'prod-aab' || github.event_name == 'push' && (matrix.platform == 'android' || github.event.inputs.platform == 'all' || github.event.inputs.platform == 'android')
213+ run : |
214+ export NODE_OPTIONS="--openssl-legacy-provider --max_old_space_size=4096"
215+ eas build --platform android --profile production --local --non-interactive --output=./app-prod.aab
216+ env :
217+ NODE_ENV : production
218+
219+ - name : 📱 Build iOS Development
220+ if : (github.event.inputs.buildType == 'all' || github.event.inputs.buildType == 'ios-dev') && (matrix.platform == 'ios' || github.event.inputs.platform == 'all' || github.event.inputs.platform == 'ios')
221+ run : |
222+ export NODE_OPTIONS="--openssl-legacy-provider --max_old_space_size=4096"
223+ eas build --platform ios --profile development --local --non-interactive --output=./app-ios-dev.app
224+ env :
225+ NODE_ENV : development
226+
227+ - name : 📱 Build iOS Production
228+ if : (github.event.inputs.buildType == 'all' || github.event.inputs.buildType == 'ios-prod') && (matrix.platform == 'ios' || github.event.inputs.platform == 'all' || github.event.inputs.platform == 'ios')
229+ run : |
230+ export NODE_OPTIONS="--openssl-legacy-provider --max_old_space_size=4096"
231+ eas build --platform ios --profile production --local --non-interactive --output=./app-ios-prod.ipa
232+ env :
233+ NODE_ENV : production
234+
235+ - name : 📦 Upload build artifacts to GitHub
236+ uses : actions/upload-artifact@v4
237+ with :
238+ name : app-builds
239+ path : |
240+ ./app-dev.apk
241+ ./app-prod.apk
242+ ./app-prod.aab
243+ ./app-ios-dev.app
244+ ./app-ios-prod.ipa
245+ retention-days : 7
0 commit comments