Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9828bbd
feat: add version management system with build number tracking
Jul 10, 2025
c84e27a
feat: integrate version.json with Fastlane deployment process
Jul 10, 2025
673da06
feat: enhance deploy confirmation with version.json info
Jul 10, 2025
0bf0cac
fix: use ENV variable directly in increment_build_number to avoid sec…
Jul 10, 2025
95768fc
fix: correct xcodeproj path for GitHub Actions workflow
Jul 10, 2025
a89200a
feat: add test mode to workflow for safe testing
Jul 10, 2025
f68b160
fix: use gradle_file_path instead of gradle_file for increment_versio…
hackertron Jul 10, 2025
04ec74a
fix: use gsub to remove ../ prefix for CI compatibility
hackertron Jul 10, 2025
795e58c
chore: remove accidentally committed files
hackertron Jul 10, 2025
1bb3c7e
feat: auto-commit version.json after successful deployment
hackertron Jul 10, 2025
feaca68
feat : update package.json in build step using npm version
hackertron Jul 14, 2025
69a3f42
feat: add comprehensive caching to mobile deployment workflow
hackertron Jul 14, 2025
28c8267
fix: move bundler config after Ruby setup in mobile-setup action
hackertron Jul 14, 2025
555defc
fix: rename cache env vars to avoid Yarn conflicts
hackertron Jul 14, 2025
47cdb5e
fix: remove bundler deployment mode to allow Gemfile updates
hackertron Jul 14, 2025
8d30040
feat: implement strict lock file enforcement (Option 1)
hackertron Jul 14, 2025
0076db0
fix: update Gemfile.lock for CI environment
hackertron Jul 14, 2025
13a76e0
fix: correct yarn.lock path for monorepo workspace
hackertron Jul 14, 2025
d7f5dfc
fix: handle both boolean and string test_mode parameter
hackertron Jul 14, 2025
07a6859
fix: address code review feedback for mobile deployment workflow
hackertron Jul 15, 2025
290cd3d
fix: formatting and linting issues
hackertron Jul 15, 2025
74412f8
feat: implement automated branch-based mobile deployments
hackertron Jul 16, 2025
d7f991a
feat: add automated git tagging and release system
hackertron Jul 16, 2025
58bd8e4
merge: resolve conflicts with dev branch
hackertron Jul 28, 2025
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
217 changes: 217 additions & 0 deletions .github/MOBILE_DEPLOYMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
# Mobile Deployment Guide
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: did we want to keep this markdown readme in a hidden folder? or should we move it to the project root or create a "docs" folder

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have kept it in folder structure since it's relevant to that folder but i agree we can move it to docs too or atleast internal docs. i have put the same markdown in notion too


This guide covers the automated mobile deployment pipeline for iOS and Android apps.

## 🚀 Quick Start

### Automatic Deployments

Deployments happen automatically when PRs are merged:

- **Merge to `dev`** → Deploy to internal testing
- **Merge to `main`** → Deploy to production

To skip deployment, add `[skip-deploy]` to your PR title or add the `no-deploy` label.

### Manual Deployments

1. Go to [Actions](../../../actions) → "Mobile App Deployments"
2. Click "Run workflow"
3. Select options:
- Platform: ios / android / both
- Test mode: Check to build without uploading
- Deployment track: internal / production
- Version bump: build / patch / minor / major

## 📋 How It Works

### Branch Strategy

```
main (production)
└── dev (internal testing)
└── feature/* (no auto-deploy)
```

### Version Management

Versions are controlled by PR labels:

- `version:major` - Bump major version (1.0.0 → 2.0.0)
- `version:minor` - Bump minor version (1.0.0 → 1.1.0)
- `version:patch` - Bump patch version (1.0.0 → 1.0.1) [default for main]
- No label on dev - Only increment build number

### Deployment Tracks

| Branch | Track | iOS Target | Android Target |
|--------|-------|------------|----------------|
| dev | internal | TestFlight Internal | Play Store Internal |
| main | production | App Store | Play Store Production |

## 🏗️ Architecture

### Workflows

1. **`mobile-deploy.yml`** - Main deployment workflow
- Handles both manual and automated deployments
- Builds and uploads to app stores
- Creates git tags for production releases

2. **`mobile-deploy-auto.yml`** - PR merge trigger
- Detects merged PRs
- Determines deployment parameters
- Calls main deployment workflow

### Version Storage

- `app/version.json` - Tracks build numbers
- `app/package.json` - Semantic version
- Native files auto-sync during build

### Caching Strategy

Build times are optimized with caching:
- Yarn dependencies
- Ruby gems
- CocoaPods (iOS)
- Gradle (Android)
- Android NDK

Average build times with cache: iOS ~15min, Android ~10min

## 🔧 Configuration

### Required Secrets

#### iOS
- `IOS_APP_IDENTIFIER` - Bundle ID
- `IOS_TEAM_ID` - Apple Team ID
- `IOS_CONNECT_KEY_ID` - App Store Connect API Key ID
- `IOS_CONNECT_ISSUER_ID` - API Key Issuer ID
- `IOS_CONNECT_API_KEY_BASE64` - API Key (base64)
- `IOS_DIST_CERT_BASE64` - Distribution certificate
- `IOS_PROV_PROFILE_BASE64` - Provisioning profile
- `IOS_P12_PASSWORD` - Certificate password

#### Android
- `ANDROID_PACKAGE_NAME` - Package name
- `ANDROID_KEYSTORE` - Keystore file (base64)
- `ANDROID_KEYSTORE_PASSWORD` - Keystore password
- `ANDROID_KEY_ALIAS` - Key alias
- `ANDROID_KEY_PASSWORD` - Key password
- `ANDROID_PLAY_STORE_JSON_KEY` - Service account key

#### Notifications
- `SLACK_API_TOKEN` - For deployment notifications
- `SLACK_CHANNEL_ID` - Channel for build uploads

### Environment Variables

Set in workflow files:
```yaml
NODE_VERSION: 18
RUBY_VERSION: 3.2
JAVA_VERSION: 17
ANDROID_API_LEVEL: 35
ANDROID_NDK_VERSION: 26.1.10909125
```

## 🏷️ Git Tags & Releases

### Automatic Tags (Production Only)

When deploying to production, creates:
- `v2.5.5` - Main version tag
- `v2.5.5-ios-148` - iOS with build number
- `v2.5.5-android-82` - Android with build number

### GitHub Releases

Automatically created for production deployments with:
- Changelog from commits
- Build information
- Links to app stores

## 🚨 Troubleshooting

### Common Issues

#### "Play Store upload failed: Insufficient permissions"
The service account needs permissions in Google Play Console. The build file is saved locally and can be uploaded manually.

#### Cache not working
- Check if lock files changed (`yarn.lock`, `Gemfile.lock`)
- Cache keys include version numbers that can be bumped
- First build on new branch may be slower

#### iOS build fails with provisioning profile error
- Ensure secrets are up to date
- Check certificate expiration
- Verify bundle ID matches

#### Version conflicts
- `version.json` tracks the source of truth
- Always higher than store versions
- Automatically incremented each build

### Build Failures

1. Check the workflow logs in GitHub Actions
2. Look for the specific error in the failed step
3. Most issues are related to:
- Expired certificates/profiles
- Missing secrets
- Network timeouts (retry usually helps)

## 📊 Monitoring

### Slack Notifications

Successful deployments post to Slack with:
- Platform and version info
- Download links for the builds
- Deployment track (internal/production)

### Deployment History

View all deployments:
1. Go to [Actions](../../../actions)
2. Filter by workflow: "Mobile App Deployments"
3. Check run history and logs

## 🔐 Security

- All secrets are stored in GitHub Secrets
- Certificates are base64 encoded
- Build artifacts are uploaded to Slack (private channel)
- Production deployments only from protected branches

## 🛠️ Maintenance

### Updating Workflows

1. Test changes with `test_mode: true`
2. Use `workflow_dispatch` for manual testing
3. Monitor first automated run carefully

### Cache Busting

If builds are failing due to cache issues:
1. Increment cache version in workflow:
```yaml
GH_CACHE_VERSION: v2 # Increment this
```

### Certificate Renewal

Before certificates expire:
1. Generate new certificates/profiles
2. Update GitHub Secrets
3. Test with manual deployment first

---

For local development and manual release processes, see [`app/README.md`](../app/README.md)
94 changes: 94 additions & 0 deletions .github/workflows/mobile-deploy-auto.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Mobile Auto Deploy

on:
pull_request:
types: [closed]
branches: [main, dev]
paths:
- 'app/**'
- '!app/**/*.md'
- '!app/docs/**'

jobs:
check-and-deploy:
if: github.event.pull_request.merged == true && !contains(github.event.pull_request.labels.*.name, 'no-deploy')
runs-on: ubuntu-latest
outputs:
should_deploy: ${{ steps.check.outputs.should_deploy }}
deployment_track: ${{ steps.check.outputs.deployment_track }}
version_bump: ${{ steps.check.outputs.version_bump }}
platforms: ${{ steps.check.outputs.platforms }}

steps:
- name: Check deployment conditions
id: check
run: |
echo "🔍 Checking deployment conditions..."

# Skip if PR has skip-deploy in title or body
if [[ "${{ github.event.pull_request.title }}" =~ \[skip-deploy\] ]] ||
[[ "${{ github.event.pull_request.body }}" =~ \[skip-deploy\] ]]; then
echo "should_deploy=false" >> $GITHUB_OUTPUT
echo "⏭️ Skipping deployment due to [skip-deploy] flag"
exit 0
fi
Comment on lines +23 to +34
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical security issue: Command injection vulnerability

The PR title is being used directly in shell scripts without proper sanitization, which can lead to command injection attacks.

Instead of using the PR title directly in the shell script, pass it through an environment variable:

      - name: Check deployment conditions
        id: check
+       env:
+         PR_TITLE: ${{ github.event.pull_request.title }}
+         PR_BODY: ${{ github.event.pull_request.body }}
        run: |
          echo "🔍 Checking deployment conditions..."
          
          # Skip if PR has skip-deploy in title or body
-         if [[ "${{ github.event.pull_request.title }}" =~ \[skip-deploy\] ]] || 
-            [[ "${{ github.event.pull_request.body }}" =~ \[skip-deploy\] ]]; then
+         if [[ "$PR_TITLE" =~ \[skip-deploy\] ]] || 
+            [[ "$PR_BODY" =~ \[skip-deploy\] ]]; then
            echo "should_deploy=false" >> $GITHUB_OUTPUT
            echo "⏭️ Skipping deployment due to [skip-deploy] flag"
            exit 0
          fi
🧰 Tools
🪛 actionlint (1.7.7)

25-25: "github.event.pull_request.title" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions for more details

(expression)

🪛 YAMLlint (1.37.1)

[error] 27-27: trailing spaces

(trailing-spaces)


[error] 29-29: trailing spaces

(trailing-spaces)

🤖 Prompt for AI Agents
In .github/workflows/mobile-deploy-auto.yml around lines 23 to 34, the PR title
and body are used directly in the shell script, causing a command injection
risk. To fix this, pass the PR title and body as environment variables to the
script step, then reference these variables inside the shell script instead of
using the GitHub context expressions directly. This avoids executing unsanitized
input in the shell.


# Determine deployment track based on target branch
if [[ "${{ github.base_ref }}" == "main" ]]; then
echo "deployment_track=production" >> $GITHUB_OUTPUT
echo "🚀 Deployment track: production"
elif [[ "${{ github.base_ref }}" == "dev" ]]; then
echo "deployment_track=internal" >> $GITHUB_OUTPUT
echo "🧪 Deployment track: internal testing"
fi

# Determine version bump from PR labels
labels="${{ join(github.event.pull_request.labels.*.name, ',') }}"
if [[ "$labels" =~ version:major ]]; then
echo "version_bump=major" >> $GITHUB_OUTPUT
echo "📈 Version bump: major"
elif [[ "$labels" =~ version:minor ]]; then
echo "version_bump=minor" >> $GITHUB_OUTPUT
echo "📈 Version bump: minor"
elif [[ "$labels" =~ version:patch ]] || [[ "${{ github.base_ref }}" == "main" ]]; then
echo "version_bump=patch" >> $GITHUB_OUTPUT
echo "📈 Version bump: patch"
else
echo "version_bump=build" >> $GITHUB_OUTPUT
echo "📈 Version bump: build only"
fi

# Always deploy both platforms for now (can be enhanced later)
echo "platforms=both" >> $GITHUB_OUTPUT
echo "should_deploy=true" >> $GITHUB_OUTPUT

- name: Log deployment info
if: steps.check.outputs.should_deploy == 'true'
run: |
echo "📱 Auto-deployment triggered!"
echo "Branch: ${{ github.base_ref }}"
echo "Track: ${{ steps.check.outputs.deployment_track }}"
echo "Version bump: ${{ steps.check.outputs.version_bump }}"
echo "PR: #${{ github.event.pull_request.number }} - ${{ github.event.pull_request.title }}"
echo "Merged by: ${{ github.event.pull_request.merged_by.login }}"
Comment on lines +65 to +73
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Security issue: Another command injection vulnerability

The PR title is used unsafely again in the logging section.

Apply the same fix by using environment variables:

      - name: Log deployment info
        if: steps.check.outputs.should_deploy == 'true'
+       env:
+         PR_TITLE: ${{ github.event.pull_request.title }}
        run: |
          echo "📱 Auto-deployment triggered!"
          echo "Branch: ${{ github.base_ref }}"
          echo "Track: ${{ steps.check.outputs.deployment_track }}"
          echo "Version bump: ${{ steps.check.outputs.version_bump }}"
-         echo "PR: #${{ github.event.pull_request.number }} - ${{ github.event.pull_request.title }}"
+         echo "PR: #${{ github.event.pull_request.number }} - $PR_TITLE"
          echo "Merged by: ${{ github.event.pull_request.merged_by.login }}"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Log deployment info
if: steps.check.outputs.should_deploy == 'true'
run: |
echo "📱 Auto-deployment triggered!"
echo "Branch: ${{ github.base_ref }}"
echo "Track: ${{ steps.check.outputs.deployment_track }}"
echo "Version bump: ${{ steps.check.outputs.version_bump }}"
echo "PR: #${{ github.event.pull_request.number }} - ${{ github.event.pull_request.title }}"
echo "Merged by: ${{ github.event.pull_request.merged_by.login }}"
- name: Log deployment info
if: steps.check.outputs.should_deploy == 'true'
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
echo "📱 Auto-deployment triggered!"
echo "Branch: ${{ github.base_ref }}"
echo "Track: ${{ steps.check.outputs.deployment_track }}"
echo "Version bump: ${{ steps.check.outputs.version_bump }}"
echo "PR: #${{ github.event.pull_request.number }} - $PR_TITLE"
echo "Merged by: ${{ github.event.pull_request.merged_by.login }}"
🧰 Tools
🪛 actionlint (1.7.7)

67-67: "github.event.pull_request.title" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions for more details

(expression)

🤖 Prompt for AI Agents
In .github/workflows/mobile-deploy-auto.yml around lines 65 to 73, the PR title
is directly echoed in the shell command, creating a command injection risk. To
fix this, assign the PR title and other dynamic values to environment variables
before the run step, then reference those variables safely within the echo
commands. This avoids executing untrusted input as shell commands.


deploy:
needs: check-and-deploy
if: needs.check-and-deploy.outputs.should_deploy == 'true'
uses: ./.github/workflows/mobile-deploy.yml
with:
platform: ${{ needs.check-and-deploy.outputs.platforms }}
deployment_track: ${{ needs.check-and-deploy.outputs.deployment_track }}
version_bump: ${{ needs.check-and-deploy.outputs.version_bump }}
auto_deploy: true
secrets: inherit

notify-skip:
needs: check-and-deploy
if: needs.check-and-deploy.outputs.should_deploy == 'false'
runs-on: ubuntu-latest
steps:
- name: Notify skip
run: |
echo "📱 Mobile deployment was skipped for this PR"
echo "To deploy manually, use the 'Run workflow' button on the Mobile App Deployments workflow"
Loading
Loading