forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 💫
1---
2name: Bundle and Deploy EAS Update
3
4on:
5 push:
6 branches:
7 - main
8 workflow_dispatch:
9 inputs:
10 channel:
11 type: choice
12 description: Deployment channel to use
13 options:
14 - testflight
15 - production
16 runtimeVersion:
17 type: string
18 description: Runtime version (in x.x.x format) that this update is for
19 required: true
20
21jobs:
22 bundleDeploy:
23 if: github.repository == 'bluesky-social/social-app'
24 name: Bundle and Deploy EAS Update
25 runs-on: ubuntu-latest
26 concurrency:
27 group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}-deploy
28 cancel-in-progress: true
29 outputs:
30 changes-detected: ${{ steps.fingerprint.outputs.includes-changes }}
31
32 steps:
33 - name: Check for EXPO_TOKEN
34 run: >
35 if [ -z "${{ secrets.EXPO_TOKEN }}" ]; then
36 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"
37 exit 1
38 fi
39
40 # Validate the version if one is supplied. This should generally happen if the update is for a production client
41 - name: 🧐 Validate version
42 env:
43 RUNTIME_VERSION: ${{ inputs.runtimeVersion }}
44 if: ${{ inputs.runtimeVersion }}
45 run: |
46 if [ -z "$RUNTIME_VERSION" ]; then
47 [[ "$RUNTIME_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && echo "Version is valid" || exit 1
48 fi
49
50 - name: ⬇️ Checkout
51 uses: actions/checkout@v4
52 with:
53 fetch-depth: 0
54
55 - name: ⬇️ Fetch commits from base branch
56 if: ${{ github.ref != 'refs/heads/main' }}
57 run: git fetch origin main:main --depth 100
58
59 - name: 🔧 Setup Node
60 uses: actions/setup-node@v6
61 with:
62 node-version-file: .nvmrc
63 cache: yarn
64
65 - name: 📷 Check fingerprint and install dependencies
66 id: fingerprint
67 uses: bluesky-social/github-actions/fingerprint-native@main
68 with:
69 profile: ${{ inputs.channel || 'testflight' }}
70 previous-commit-tag: ${{ inputs.runtimeVersion }}
71
72 - name: Lint check
73 run: yarn lint
74
75 - name: Lint lockfile
76 run: yarn lockfile-lint
77
78 - name: Prettier check
79 run: yarn prettier --check .
80
81 - name: 🔤 Compile translations
82 run: yarn intl:build 2>&1 | tee i18n.log
83
84 - name: Check for i18n compilation errors
85 run: if grep -q "invalid syntax" "i18n.log"; then echo "\n\nFound compilation errors!\n\n" && exit 1; else echo "\n\nNo compilation errors!\n\n"; fi
86
87 - name: Type check
88 run: yarn typecheck
89
90 - name: 🔨 Setup EAS
91 uses: expo/expo-github-action@main
92 if: ${{ !steps.fingerprint.outputs.includes-changes }}
93 with:
94 expo-version: latest
95 eas-version: latest
96 token: ${{ secrets.EXPO_TOKEN }}
97
98 - name: ⛏️ Setup Expo
99 if: ${{ !steps.fingerprint.outputs.includes-changes }}
100 run: yarn global add eas-cli-local-build-plugin
101
102 - name: 🪛 Setup jq
103 if: ${{ !steps.fingerprint.outputs.includes-changes }}
104 uses: dcarbone/install-jq-action@v2
105
106 # eas.json not used here, set EXPO_PUBLIC_ENV
107 - name: Env
108 env:
109 CHANNEL: ${{ inputs.channel || 'testflight' }}
110 GITHUB_SHA: ${{ github.sha }}
111 id: env
112 if: ${{ !steps.fingerprint.outputs.includes-changes }}
113 run: |
114 export json='${{ secrets.GOOGLE_SERVICES_TOKEN }}'
115 echo "${{ secrets.ENV_TOKEN }}" > .env
116 echo "EXPO_PUBLIC_ENV=$CHANNEL" >> .env
117 echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> .env
118 echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT
119 echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> .env
120 echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
121 echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env
122 echo "EXPO_PUBLIC_SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> .env
123 echo "EXPO_PUBLIC_BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env
124 echo "EXPO_PUBLIC_GCP_PROJECT_ID=${{ secrets.EXPO_PUBLIC_GCP_PROJECT_ID }}" >> .env
125 echo "$json" > google-services.json
126
127 - name: 🏗️ Create Bundle
128 if: ${{ !steps.fingerprint.outputs.includes-changes }}
129 run: >
130 SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
131 SENTRY_RELEASE=${{ steps.env.outputs.EXPO_PUBLIC_RELEASE_VERSION }}
132 SENTRY_DIST=${{ steps.env.outputs.EXPO_PUBLIC_BUNDLE_IDENTIFIER }}
133 yarn export
134
135 - name: 📦 Package Bundle and 🚀 Deploy
136 if: ${{ !steps.fingerprint.outputs.includes-changes }}
137 run: yarn use-build-number bash scripts/bundleUpdate.sh
138 env:
139 DENIS_API_KEY: ${{ secrets.DENIS_API_KEY }}
140 RUNTIME_VERSION: ${{ inputs.runtimeVersion }}
141 CHANNEL_NAME: ${{ inputs.channel || 'testflight' }}
142
143 - name: ⬇️ Restore Cache
144 id: get-base-commit
145 uses: actions/cache@v4
146 if: ${{ !steps.fingerprint.outputs.includes-changes }}
147 with:
148 path: most-recent-testflight-commit.txt
149 key: most-recent-testflight-commit
150
151 - name: ✏️ Write commit hash to cache
152 if: ${{ !steps.fingerprint.outputs.includes-changes }}
153 run: echo $GITHUB_SHA > most-recent-testflight-commit.txt
154
155 # GitHub actions are horrible so let's just copy paste this in
156 buildIfNecessaryIOS:
157 name: Build and Submit iOS
158 runs-on: macos-26
159 concurrency:
160 group: ios-build
161 cancel-in-progress: false
162 needs: [bundleDeploy]
163 # Gotta check if its NOT '[]' because any md5 hash in the outputs is detected as a possible secret and won't be
164 # available here
165 if: ${{ inputs.channel != 'production' && needs.bundleDeploy.outputs.changes-detected && github.repository == 'bluesky-social/social-app' }}
166 steps:
167 - name: Check for EXPO_TOKEN
168 run: >
169 if [ -z "${{ secrets.EXPO_TOKEN }}" ]; then
170 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"
171 exit 1
172 fi
173
174 - name: ⬇️ Checkout
175 uses: actions/checkout@v4
176 with:
177 fetch-depth: 5
178
179 - name: 🔧 Setup Node
180 uses: actions/setup-node@v6
181 with:
182 node-version-file: .nvmrc
183 cache: yarn
184
185 - name: 🔨 Setup EAS
186 uses: expo/expo-github-action@main
187 with:
188 expo-version: latest
189 eas-version: latest
190 token: ${{ secrets.EXPO_TOKEN }}
191
192 - name: ⛏️ Setup EAS local builds
193 run: yarn global add eas-cli-local-build-plugin
194
195 - name: ⚙️ Install dependencies
196 run: yarn install --frozen-lockfile
197
198 - uses: maxim-lobanov/setup-xcode@v1
199 with:
200 xcode-version: "26.0"
201
202 - name: ☕️ Setup Cocoapods
203 uses: maxim-lobanov/setup-cocoapods@v1
204 with:
205 version: 1.16.2
206
207 - name: 💾 Cache Pods
208 uses: actions/cache@v4
209 id: pods-cache
210 with:
211 path: ./ios/Pods
212 # We'll use the yarn.lock for our hash since we don't yet have a Podfile.lock. Pod versions will not
213 # change unless the yarn version changes as well.
214 key: ${{ runner.os }}-pods-${{ hashFiles('yarn.lock') }}
215
216 - name: 🔤 Compile translations
217 run: yarn intl:build
218
219 # EXPO_PUBLIC_ENV is handled in eas.json
220 - name: Env
221 id: env
222 run: |
223 echo "${{ secrets.ENV_TOKEN }}" > .env
224 echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> .env
225 echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT
226 echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> .env
227 echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
228 echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env
229 echo "EXPO_PUBLIC_SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> .env
230 echo "EXPO_PUBLIC_BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env
231 echo "EXPO_PUBLIC_GCP_PROJECT_ID=${{ secrets.EXPO_PUBLIC_GCP_PROJECT_ID }}" >> .env
232 echo "${{ secrets.GOOGLE_SERVICES_TOKEN }}" > google-services.json
233
234 - name: 🏗️ EAS Build
235 run: >
236 SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
237 SENTRY_RELEASE=${{ steps.env.outputs.EXPO_PUBLIC_RELEASE_VERSION }}
238 SENTRY_DIST=${{ steps.env.outputs.EXPO_PUBLIC_BUNDLE_IDENTIFIER }}
239 yarn use-build-number-with-bump
240 eas build -p ios
241 --profile testflight
242 --local --output build.ipa --non-interactive
243
244 - name: 🚀 Deploy
245 run: eas submit -p ios --non-interactive --path build.ipa
246
247 - name: ⬇️ Restore Cache
248 id: get-base-commit
249 uses: actions/cache@v4
250 if: ${{ inputs.channel == 'testflight' }}
251 with:
252 path: most-recent-testflight-commit.txt
253 key: most-recent-testflight-commit
254
255 - name: ✏️ Write commit hash to cache
256 if: ${{ inputs.channel == 'testflight' }}
257 env:
258 GITHUB_SHA: ${{ github.sha }}
259 run: echo $GITHUB_SHA > most-recent-testflight-commit.txt
260
261 buildIfNecessaryAndroid:
262 name: Build and Submit Android
263 runs-on: ubuntu-latest
264 concurrency:
265 group: android-build
266 cancel-in-progress: false
267 needs: [bundleDeploy]
268 # Gotta check if its NOT '[]' because any md5 hash in the outputs is detected as a possible secret and won't be
269 # available here
270 if: ${{ inputs.channel != 'production' && needs.bundleDeploy.outputs.changes-detected && github.repository == 'bluesky-social/social-app'}}
271
272 steps:
273 - name: Check for EXPO_TOKEN
274 run: >
275 if [ -z "${{ secrets.EXPO_TOKEN }}" ]; then
276 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"
277 exit 1
278 fi
279
280 - name: ⬇️ Checkout
281 uses: actions/checkout@v4
282 with:
283 fetch-depth: 5
284
285 - name: 🔧 Setup Node
286 uses: actions/setup-node@v6
287 with:
288 node-version-file: .nvmrc
289 cache: yarn
290
291 - name: 🔨 Setup EAS
292 uses: expo/expo-github-action@main
293 with:
294 expo-version: latest
295 eas-version: latest
296 token: ${{ secrets.EXPO_TOKEN }}
297
298 - name: ⛏️ Setup EAS local builds
299 run: yarn global add eas-cli-local-build-plugin
300
301 - uses: actions/setup-java@v4
302 with:
303 distribution: "temurin"
304 java-version: "17"
305
306 - name: ⚙️ Install dependencies
307 run: yarn install --frozen-lockfile
308
309 - name: 🔤 Compile translations
310 run: yarn intl:build
311
312 # EXPO_PUBLIC_ENV is handled in eas.json
313 - name: Env
314 id: env
315 run: |
316 export json='${{ secrets.GOOGLE_SERVICES_TOKEN }}'
317 echo "${{ secrets.ENV_TOKEN }}" > .env
318 echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> .env
319 echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT
320 echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> .env
321 echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
322 echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env
323 echo "EXPO_PUBLIC_SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> .env
324 echo "EXPO_PUBLIC_BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env
325 echo "EXPO_PUBLIC_GCP_PROJECT_ID=${{ secrets.EXPO_PUBLIC_GCP_PROJECT_ID }}" >> .env
326 echo "$json" > google-services.json
327
328 - name: 🏗️ EAS Build
329 run: >
330 SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
331 SENTRY_RELEASE=${{ steps.env.outputs.EXPO_PUBLIC_RELEASE_VERSION }}
332 SENTRY_DIST=${{ steps.env.outputs.EXPO_PUBLIC_BUNDLE_IDENTIFIER }}
333 yarn use-build-number-with-bump
334 eas build -p android
335 --profile testflight-android
336 --local --output build.apk --non-interactive
337
338 - name: ⏰ Get a timestamp
339 id: timestamp
340 uses: nanzm/get-time-action@master
341 with:
342 format: "MM-DD-HH-mm-ss"
343
344 - name: 🚀 Upload Artifact
345 id: upload-artifact
346 uses: actions/upload-artifact@v4
347 with:
348 retention-days: 30
349 compression-level: 0
350 name: build-${{ steps.timestamp.outputs.time }}.apk
351 path: build.apk
352
353 - name: 🔔 Notify Slack
354 uses: slackapi/slack-github-action@v1.25.0
355 with:
356 payload: |
357 {
358 "text": "Android build is ready for testing. Download the artifact here: ${{ steps.upload-artifact.outputs.artifact-url }}"
359 }
360 env:
361 SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CLIENT_ALERT_WEBHOOK }}
362 SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
363
364 - name: ⬇️ Restore Cache
365 id: get-base-commit
366 uses: actions/cache@v4
367 if: ${{ inputs.channel != 'testflight' && inputs.channel != 'production' }}
368 with:
369 path: most-recent-testflight-commit.txt
370 key: most-recent-testflight-commit
371
372 - name: ✏️ Write commit hash to cache
373 env:
374 GITHUB_SHA: ${{ github.sha }}
375 if: ${{ inputs.channel != 'testflight' && inputs.channel != 'production' }}
376 run: echo $GITHUB_SHA > most-recent-testflight-commit.txt