-
Notifications
You must be signed in to change notification settings - Fork 180
Feat/push to dev main #767
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9828bbd
c84e27a
673da06
0bf0cac
95768fc
a89200a
f68b160
04ec74a
795e58c
1bb3c7e
feaca68
69a3f42
28c8267
555defc
47cdb5e
8d30040
0076db0
13a76e0
d7f5dfc
07a6859
290cd3d
74412f8
d7f991a
58bd8e4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,217 @@ | ||
| # Mobile Deployment Guide | ||
|
|
||
| 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) | ||
| 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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 |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| # 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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
Suggested change
🧰 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 |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| 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" | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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