Skip to content
Merged
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
211 changes: 170 additions & 41 deletions .github/workflows/mobile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,74 @@ env:
NODE_VERSION: '22.21.1'

jobs:
# Initialize: Install dependencies, lint & typecheck
mobile-init:
name: Mobile Init (Install, Lint & Typecheck)
# Install dependencies and warm the node_modules cache shared by every downstream job.
# Kept separate from lint/typecheck so OTA + build jobs don't wait on verify (~3.5 min).
mobile-install:
name: Mobile Install
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: package-lock.json

- name: Create concatenated patch file
id: patch-file
run: |
ls -d -- packages/*/patches/*.patch 2>/dev/null | xargs cat > combined-patch-file.txt || touch combined-patch-file.txt
echo "patch_checksum=$(sha256sum combined-patch-file.txt | cut -d' ' -f1)" >> $GITHUB_OUTPUT

- name: Cache node modules
id: cache-node-modules
uses: actions/cache@v4
with:
path: |
node_modules
packages/mobile/node_modules
packages/common/node_modules
packages/libs/node_modules
packages/libs/dist
packages/sdk/node_modules
packages/sdk/dist
packages/harmony/node_modules
packages/dotenv-linter/bin
key: npm-cache-mobile-${{ runner.os }}-node-${{ env.NODE_VERSION }}-${{ hashFiles('package-lock.json') }}-${{ steps.patch-file.outputs.patch_checksum }}
restore-keys: |
npm-cache-mobile-${{ runner.os }}-node-${{ env.NODE_VERSION }}-${{ hashFiles('package-lock.json') }}-
npm-cache-mobile-${{ runner.os }}-${{ hashFiles('package-lock.json') }}-

- name: Install dependencies
env:
CI: true
NODE_OPTIONS: --max-old-space-size=8192
run: |
if [[ -d node_modules ]]; then
echo "Using cached node_modules, running postinstall..."
npm run postinstall
# Ensure dotenv-linter binary is installed (install script downloads it)
if [[ ! -f packages/dotenv-linter/bin/dotenv-linter ]]; then
echo "dotenv-linter binary missing, running install script..."
(cd packages/dotenv-linter && npm run install)
fi
else
echo "No cache found, running fresh install..."
# Clear npm cache to avoid EEXIST conflicts
npm cache clean --force || true
# Try npm ci first, fallback to npm install if lock file is out of sync
npm ci --prefer-offline || npm install --prefer-offline
fi

# Verify: lint & typecheck. Runs in parallel with OTA/build jobs — failure still fails the workflow,
# but does not block the OTA publish on the critical path.
mobile-verify:
name: Mobile Verify (Lint & Typecheck)
runs-on: ubuntu-latest
needs: mobile-install
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand Down Expand Up @@ -81,16 +145,13 @@ jobs:
if [[ -d node_modules ]]; then
echo "Using cached node_modules, running postinstall..."
npm run postinstall
# Ensure dotenv-linter binary is installed (install script downloads it)
if [[ ! -f packages/dotenv-linter/bin/dotenv-linter ]]; then
echo "dotenv-linter binary missing, running install script..."
(cd packages/dotenv-linter && npm run install)
fi
else
echo "No cache found, running fresh install..."
# Clear npm cache to avoid EEXIST conflicts
npm cache clean --force || true
# Try npm ci first, fallback to npm install if lock file is out of sync
npm ci --prefer-offline || npm install --prefer-offline
fi

Expand All @@ -100,7 +161,7 @@ jobs:
mobile-version-check:
name: Mobile Version Change Check
runs-on: ubuntu-latest
needs: mobile-init
needs: mobile-install
if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'workflow_dispatch'
outputs:
current_version: ${{ steps.version-check.outputs.current_version }}
Expand Down Expand Up @@ -129,12 +190,17 @@ jobs:
echo "version_unchanged=false" >> "$GITHUB_OUTPUT"
fi

# OTA Release: publish JS bundle updates when native app version is unchanged
# OTA Release: publish JS bundle updates when native app version is unchanged.
# Matrixed over platforms so iOS and Android publish in parallel.
mobile-ota-release:
name: Mobile OTA Release (CodePush)
name: Mobile OTA Release (CodePush, ${{ matrix.platform }})
runs-on: ubuntu-latest
needs: [mobile-init, mobile-version-check]
needs: [mobile-install, mobile-version-check]
if: (github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.mobile-version-check.outputs.version_unchanged == 'true') || (github.event_name == 'workflow_dispatch' && (github.event.inputs.ota_channel == 'rc' || github.event.inputs.ota_channel == ''))
strategy:
fail-fast: false
matrix:
platform: [ios, android]
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand All @@ -148,11 +214,48 @@ jobs:
cache: 'npm'
cache-dependency-path: package-lock.json

- name: Create concatenated patch file
id: patch-file
run: |
ls -d -- packages/*/patches/*.patch 2>/dev/null | xargs cat > combined-patch-file.txt || touch combined-patch-file.txt
echo "patch_checksum=$(sha256sum combined-patch-file.txt | cut -d' ' -f1)" >> $GITHUB_OUTPUT

- name: Cache node modules
id: cache-node-modules
uses: actions/cache@v4
with:
path: |
node_modules
packages/mobile/node_modules
packages/common/node_modules
packages/libs/node_modules
packages/libs/dist
packages/sdk/node_modules
packages/sdk/dist
packages/harmony/node_modules
packages/dotenv-linter/bin
key: npm-cache-mobile-${{ runner.os }}-node-${{ env.NODE_VERSION }}-${{ hashFiles('package-lock.json') }}-${{ steps.patch-file.outputs.patch_checksum }}
restore-keys: |
npm-cache-mobile-${{ runner.os }}-node-${{ env.NODE_VERSION }}-${{ hashFiles('package-lock.json') }}-
npm-cache-mobile-${{ runner.os }}-${{ hashFiles('package-lock.json') }}-

- name: Install dependencies
env:
CI: true
NODE_OPTIONS: --max-old-space-size=8192
run: npm ci --prefer-offline || npm install --prefer-offline
run: |
if [[ -d node_modules ]]; then
echo "Using cached node_modules, running postinstall..."
npm run postinstall
if [[ ! -f packages/dotenv-linter/bin/dotenv-linter ]]; then
echo "dotenv-linter binary missing, running install script..."
(cd packages/dotenv-linter && npm run install)
fi
else
echo "No cache found, running fresh install..."
npm cache clean --force || true
npm ci --prefer-offline || npm install --prefer-offline
fi

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
Expand All @@ -170,7 +273,7 @@ jobs:
echo "channel=rc" >> "$GITHUB_OUTPUT"
fi

- name: Publish OTA updates (iOS + Android)
- name: Publish OTA update (${{ matrix.platform }})
env:
OTA_S3_BUCKET: ${{ secrets.OTA_S3_BUCKET }}
OTA_S3_PREFIX: mobile-ota
Expand All @@ -185,32 +288,29 @@ jobs:
OTA_APP_VERSION="${MAJOR}.${MINOR}.${OTA_PATCH}"

cd packages/mobile
npx code-push create-history --binary-version "$BINARY_VERSION" --platform ios --identifier "$OTA_CHANNEL" || true
npx code-push create-history --binary-version "$BINARY_VERSION" --platform android --identifier "$OTA_CHANNEL" || true

npx code-push release \
--binary-version "$BINARY_VERSION" \
--app-version "$OTA_APP_VERSION" \
--platform ios \
--identifier "$OTA_CHANNEL" \
--entry-file index.js
npx code-push create-history --binary-version "$BINARY_VERSION" --platform ${{ matrix.platform }} --identifier "$OTA_CHANNEL" || true

npx code-push release \
--binary-version "$BINARY_VERSION" \
--app-version "$OTA_APP_VERSION" \
--platform android \
--platform ${{ matrix.platform }} \
--identifier "$OTA_CHANNEL" \
--entry-file index.js

# OTA Release (Production): manual + environment approval gate
# OTA Release (Production): manual + environment approval gate.
# Matrixed over platforms so iOS and Android publish in parallel.
mobile-ota-release-production:
name: Mobile OTA Release (Production, Approved)
name: Mobile OTA Release (Production, ${{ matrix.platform }})
runs-on: ubuntu-latest
needs: [mobile-init, mobile-version-check]
needs: [mobile-install, mobile-version-check]
if: github.event_name == 'workflow_dispatch' && github.event.inputs.ota_channel == 'production'
environment:
name: mobile-production-ota
url: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
strategy:
fail-fast: false
matrix:
platform: [ios, android]
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand All @@ -224,11 +324,48 @@ jobs:
cache: 'npm'
cache-dependency-path: package-lock.json

- name: Create concatenated patch file
id: patch-file
run: |
ls -d -- packages/*/patches/*.patch 2>/dev/null | xargs cat > combined-patch-file.txt || touch combined-patch-file.txt
echo "patch_checksum=$(sha256sum combined-patch-file.txt | cut -d' ' -f1)" >> $GITHUB_OUTPUT

- name: Cache node modules
id: cache-node-modules
uses: actions/cache@v4
with:
path: |
node_modules
packages/mobile/node_modules
packages/common/node_modules
packages/libs/node_modules
packages/libs/dist
packages/sdk/node_modules
packages/sdk/dist
packages/harmony/node_modules
packages/dotenv-linter/bin
key: npm-cache-mobile-${{ runner.os }}-node-${{ env.NODE_VERSION }}-${{ hashFiles('package-lock.json') }}-${{ steps.patch-file.outputs.patch_checksum }}
restore-keys: |
npm-cache-mobile-${{ runner.os }}-node-${{ env.NODE_VERSION }}-${{ hashFiles('package-lock.json') }}-
npm-cache-mobile-${{ runner.os }}-${{ hashFiles('package-lock.json') }}-

- name: Install dependencies
env:
CI: true
NODE_OPTIONS: --max-old-space-size=8192
run: npm ci --prefer-offline || npm install --prefer-offline
run: |
if [[ -d node_modules ]]; then
echo "Using cached node_modules, running postinstall..."
npm run postinstall
if [[ ! -f packages/dotenv-linter/bin/dotenv-linter ]]; then
echo "dotenv-linter binary missing, running install script..."
(cd packages/dotenv-linter && npm run install)
fi
else
echo "No cache found, running fresh install..."
npm cache clean --force || true
npm ci --prefer-offline || npm install --prefer-offline
fi

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
Expand All @@ -237,7 +374,7 @@ jobs:
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1

- name: Publish OTA updates to production (iOS + Android)
- name: Publish OTA update to production (${{ matrix.platform }})
env:
OTA_S3_BUCKET: ${{ secrets.OTA_S3_BUCKET }}
OTA_S3_PREFIX: mobile-ota
Expand All @@ -251,28 +388,20 @@ jobs:
OTA_APP_VERSION="${MAJOR}.${MINOR}.${OTA_PATCH}"

cd packages/mobile
npx code-push create-history --binary-version "$BINARY_VERSION" --platform ios --identifier "$OTA_CHANNEL" || true
npx code-push create-history --binary-version "$BINARY_VERSION" --platform android --identifier "$OTA_CHANNEL" || true

npx code-push release \
--binary-version "$BINARY_VERSION" \
--app-version "$OTA_APP_VERSION" \
--platform ios \
--identifier "$OTA_CHANNEL" \
--entry-file index.js
npx code-push create-history --binary-version "$BINARY_VERSION" --platform ${{ matrix.platform }} --identifier "$OTA_CHANNEL" || true

npx code-push release \
--binary-version "$BINARY_VERSION" \
--app-version "$OTA_APP_VERSION" \
--platform android \
--platform ${{ matrix.platform }} \
--identifier "$OTA_CHANNEL" \
--entry-file index.js

# iOS Release Candidate: Build and upload
mobile-build-upload-releasecandidate-ios:
name: iOS Release Candidate Build & Upload
runs-on: macos-15
needs: [mobile-init, mobile-version-check]
needs: [mobile-install, mobile-version-check]
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' && needs.mobile-version-check.outputs.version_changed == 'true'
steps:
- name: Checkout code
Expand Down Expand Up @@ -446,7 +575,7 @@ jobs:
mobile-build-upload-releasecandidate-android:
name: Android Release Candidate Build & Upload
runs-on: ubuntu-latest
needs: [mobile-init, mobile-version-check]
needs: [mobile-install, mobile-version-check]
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' && needs.mobile-version-check.outputs.version_changed == 'true'
steps:
- name: Checkout code
Expand Down Expand Up @@ -662,7 +791,7 @@ jobs:
mobile-build-upload-production-ios-main:
name: iOS Production Build & Upload
runs-on: macos-15
needs: [mobile-init, mobile-version-check]
needs: [mobile-install, mobile-version-check]
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' && needs.mobile-version-check.outputs.version_changed == 'true'
environment:
name: mobile-production-ios
Expand Down Expand Up @@ -823,7 +952,7 @@ jobs:
mobile-build-upload-production-android-main:
name: Android Production Build & Upload
runs-on: ubuntu-latest
needs: [mobile-init, mobile-version-check]
needs: [mobile-install, mobile-version-check]
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' && needs.mobile-version-check.outputs.version_changed == 'true'
environment:
name: mobile-production-android
Expand Down
Loading