Skip to content

Release Workflow

Release Workflow #165

Workflow file for this run

name: Release Workflow
on:
workflow_dispatch:
inputs:
version_type:
description: "Type of version bump (major, minor, patch, premajor, preminor, prepatch, prerelease)"
required: true
default: "patch"
type: choice
options:
- major
- minor
- patch
- premajor
- preminor
- prepatch
- prerelease
ios_only:
description: "iOS only: skip version bump + Android, build TestFlight from current ref"
required: false
default: false
type: boolean
jobs:
# Job 1: Version Bumping and Android Build
build_android:
if: ${{ !inputs.ios_only }}
runs-on: ubuntu-latest
permissions:
contents: 'write' # Allows workflow to checkout repository code
id-token: 'write' # Required for Google Cloud Workload Identity Federation authentication (OIDC token generation)
steps:
# Step 1: Checkout the code
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Important for git history
ssh-key: ${{ secrets.DEPLOY_KEY }}
# Step 2: Set up JDK 17
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
# Step 3: Set up Node.js
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '22.21.0' # Use the specified Node.js version
cache: 'yarn'
# Step 4: Install dependencies using Yarn
- name: Install dependencies
run: yarn install
# Step 5: Set up Ruby and Bundler
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2.3' # Specify a Ruby version
bundler-cache: true
# Step 6: Combined version bump
- name: Bump versions
working-directory: ${{ github.workspace }}
run: bundle exec fastlane bump_version version_type:${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version_type || 'patch' }}
# Step 7: Commit and push version changes
- name: Commit and push version changes
run: |
VERSION=$(cat .version)
echo "VERSION=$VERSION" >> $GITHUB_ENV
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add .version package.json android/app/build.gradle ios/PocketPal.xcodeproj/project.pbxproj
git commit -m "chore(release): bump version to $VERSION"
git push
git tag "v$VERSION"
git push origin "v$VERSION"
# Step 8: Set up Android Keystore
- name: Set up Android Keystore
working-directory: android
run: |
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > app/pocketpal-release-key.keystore
# Step 9: Authenticate to Google Cloud
- name: Authenticate to Google Cloud
id: auth
uses: google-github-actions/auth@v2
with:
service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
workload_identity_provider: ${{ secrets.WORKLOAD_IDENTITY_PROVIDER }}
create_credentials_file: true
# Add this step before the Android build
- name: Create .env file
run: |
cat > .env << 'EOL'
FIREBASE_FUNCTIONS_URL=${{ vars.FIREBASE_FUNCTIONS_URL }}
# PalsHub/Supabase Configuration
SUPABASE_URL=${{ vars.SUPABASE_URL }}
SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }}
PALSHUB_API_BASE_URL=${{ vars.PALSHUB_API_BASE_URL }}
# App Configuration
APP_URL=pocketpal://app
# Feature Flags (true/false)
ENABLE_PALSHUB_INTEGRATION=true
ENABLE_AUTHENTICATION=true
ENABLE_OFFLINE_MODE=true
# Google Sign-In Configuration
GOOGLE_IOS_CLIENT_ID=${{ vars.GOOGLE_IOS_CLIENT_ID }}
GOOGLE_WEB_CLIENT_ID=${{ vars.GOOGLE_WEB_CLIENT_ID }}
EOL
# Step 10: Build and upload Android app to Alpha track (includes building APK and Bundle)
- name: Build and upload Android app
working-directory: android
env:
KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
GRADLE_USER_HOME: ${{ runner.temp }}/.gradle
# GOOGLE_APPLICATION_CREDENTIALS: ${{ steps.auth.outputs.credentials_file_path }} # This is not supported by fastlane, we need to replace it with PLAY_STORE_JSON_KEY in the future.
PLAY_STORE_JSON_KEY: ${{ secrets.PLAY_STORE_SERVICE_ACCOUNT_JSON }}
GOOGLE_SERVICES_JSON: ${{ secrets.GOOGLE_SERVICES_JSON }}
run: |
echo "$PLAY_STORE_JSON_KEY" > play-store-key.json
bundle exec fastlane release_android_alpha
# Step 11: Create GitHub Release with APK
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
files: android/app/build/outputs/apk/prod/release/app-prod-release.apk
tag_name: "v${{ env.VERSION }}"
name: "Release v${{ env.VERSION }}"
draft: false
prerelease: false
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Job 2: iOS Build and Upload (runs on macOS)
build_ios:
runs-on: macos-26 # macOS 26 includes an iOS 26-capable Xcode toolchain
needs: build_android
if: ${{ always() && (needs.build_android.result == 'success' || needs.build_android.result == 'skipped') }}
env:
NODE_OPTIONS: '--max-old-space-size=8192'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.ref }} # This ensures we get the latest changes including the version bump
fetch-depth: 0
# Step 1: Select the default Xcode shipped on macOS 26.
# App Store Connect now requires uploads built with the iOS 26 SDK or later.
- name: Setup Xcode
run: |
sudo xcode-select -s /Applications/Xcode.app
xcodebuild -version
# Step 2: Set up Node.js
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '22.21.0'
cache: 'yarn'
# Step 3: Install dependencies using Yarn
- name: Install dependencies
run: yarn install
# Step 4: Set up Ruby and Bundler
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2.3' # Specify a Ruby version
bundler-cache: true
# Step 5: Install CocoaPods dependencies
- name: Install CocoaPods dependencies
working-directory: ios
run: pod install
# Step 6: Create Env.xcconfig
- name: Create Env.xcconfig
run: |
cat > ios/Config/Env.xcconfig << 'EOL'
// This file contains environment-specific build settings.
GOOGLE_WEB_CLIENT_ID = ${{ vars.GOOGLE_WEB_CLIENT_ID }}
REVERSED_GOOGLE_IOS_CLIENT_ID = ${{ vars.REVERSED_GOOGLE_IOS_CLIENT_ID }}
EOL
# Step 7: Create .env file
- name: Create .env file
run: |
cat > .env << 'EOL'
FIREBASE_FUNCTIONS_URL=${{ vars.FIREBASE_FUNCTIONS_URL }}
# PalsHub/Supabase Configuration
SUPABASE_URL=${{ vars.SUPABASE_URL }}
SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }}
PALSHUB_API_BASE_URL=${{ vars.PALSHUB_API_BASE_URL }}
# App Configuration
APP_URL=pocketpal://app
# Feature Flags (true/false)
ENABLE_PALSHUB_INTEGRATION=true
ENABLE_AUTHENTICATION=true
ENABLE_OFFLINE_MODE=true
# Google Sign-In Configuration
GOOGLE_IOS_CLIENT_ID=${{ vars.GOOGLE_IOS_CLIENT_ID }}
GOOGLE_WEB_CLIENT_ID=${{ vars.GOOGLE_WEB_CLIENT_ID }}
EOL
# Step 8: Build and upload iOS app to TestFlight
- name: Build and upload iOS app
working-directory: ios
env:
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
MATCH_GITHUB_TOKEN: ${{ secrets.MATCH_GITHUB_TOKEN }}
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
APP_STORE_CONNECT_API_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_ISSUER_ID }}
APP_STORE_CONNECT_API_KEY_CONTENT: ${{ secrets.APP_STORE_CONNECT_API_KEY_CONTENT }}
APP_STORE_CONNECT_USER_ID: ${{ secrets.APP_STORE_CONNECT_USER_ID }}
GOOGLE_SERVICES_PLIST: ${{ secrets.GOOGLE_SERVICES_PLIST }}
run: bundle exec fastlane release_ios_testflight
# Step 9: Extract iOS version info for dSYM artifact naming
- name: Extract iOS version info
id: version
if: always()
run: |
MARKETING_VERSION=$(cat .version | tr -d '[:space:]')
BUILD_NUMBER=$(grep -m1 'CURRENT_PROJECT_VERSION' ios/PocketPal.xcodeproj/project.pbxproj | awk -F'= ' '{print $2}' | tr -d ' ;')
echo "marketing_version=${MARKETING_VERSION}" >> "$GITHUB_OUTPUT"
echo "build_number=${BUILD_NUMBER}" >> "$GITHUB_OUTPUT"
echo "Extracted: v${MARKETING_VERSION} build ${BUILD_NUMBER}"
# Step 10: Retain dSYM as a workflow artifact for crash symbolication (FOU-86)
- name: Upload iOS dSYM
if: always()
uses: actions/upload-artifact@v4
with:
name: ios-dsym-v${{ steps.version.outputs.marketing_version }}-build${{ steps.version.outputs.build_number }}
path: ios/build/PocketPal.app.dSYM.zip
retention-days: 90
if-no-files-found: warn