diff --git a/.github/actions/generate-changelog/action.yml b/.github/actions/generate-changelog/action.yml deleted file mode 100644 index 55c65fb4a..000000000 --- a/.github/actions/generate-changelog/action.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: 'Generate Changelog' -description: 'Generates a changelog from conventional commits since the last successful build' - -inputs: - workflow: - description: 'The workflow file name to check for last successful run (e.g., qa.yml)' - required: true - -outputs: - changelog: - description: 'The generated changelog formatted for Slack' - value: ${{ steps.changelog.outputs.changelog }} - -runs: - using: 'composite' - steps: - - name: Get last successful run - id: last_run - uses: ./.github/actions/get-last-successful-run - with: - workflow: ${{ inputs.workflow }} - - - name: Generate Changelog - id: changelog - shell: bash - run: | - LAST_SHA="${{ steps.last_run.outputs.sha }}" - - if [ -n "$LAST_SHA" ] && git cat-file -t "$LAST_SHA" >/dev/null 2>&1; then - echo "Found last successful build at commit: $LAST_SHA" - LAST_REF="$LAST_SHA" - else - echo "No previous successful build found, using initial commit" - LAST_REF=$(git rev-list --max-parents=0 HEAD) - fi - - EOF=$(dd if=/dev/urandom bs=15 count=1 2>/dev/null | base64) - { - echo "changelog<<$EOF" - ./tools/generate_changelog.sh "$LAST_REF" - echo "$EOF" - } >> "$GITHUB_OUTPUT" diff --git a/.github/actions/get-last-successful-run/action.yml b/.github/actions/get-last-successful-run/action.yml deleted file mode 100644 index 4e919af84..000000000 --- a/.github/actions/get-last-successful-run/action.yml +++ /dev/null @@ -1,73 +0,0 @@ -name: 'Get Last Successful Run' -description: 'Finds the SHA of the last successful workflow run on the current branch' - -inputs: - workflow: - description: 'The workflow file name (e.g., qa.yml or release.yml)' - required: true - event: - description: 'Optional event type to filter by (e.g., schedule, workflow_dispatch)' - required: false - default: '' - -outputs: - sha: - description: 'The commit SHA of the last successful run (empty if none found)' - value: ${{ steps.find.outputs.sha }} - has_changes: - description: 'Whether there are changes since the last successful run' - value: ${{ steps.find.outputs.has_changes }} - -runs: - using: 'composite' - steps: - - name: Find last successful run - id: find - shell: bash - env: - GH_TOKEN: ${{ github.token }} - run: | - BRANCH="${GITHUB_REF_NAME}" - WORKFLOW="${{ inputs.workflow }}" - EVENT="${{ inputs.event }}" - CURRENT_SHA="${GITHUB_SHA}" - - echo "Looking for last successful run of ${WORKFLOW} on branch ${BRANCH}..." - if [ -n "$EVENT" ]; then - echo "Filtering by event: ${EVENT}" - fi - - # Build the gh run list command - GH_ARGS=( - --workflow="${WORKFLOW}" - --branch="${BRANCH}" - --status=success - --limit=10 - --json headSha - --jq '.[0].headSha // empty' - ) - - # Add event filter if specified - if [ -n "$EVENT" ]; then - GH_ARGS+=(--event="${EVENT}") - fi - - LAST_SHA=$(gh run list "${GH_ARGS[@]}" 2>/dev/null || echo "") - - echo "Last successful run SHA: ${LAST_SHA:-none}" - echo "Current SHA: $CURRENT_SHA" - - # Output the SHA (may be empty) - echo "sha=$LAST_SHA" >> $GITHUB_OUTPUT - - # Determine if there are changes - if [ -z "$LAST_SHA" ]; then - echo "No previous successful run found - has changes" - echo "has_changes=true" >> $GITHUB_OUTPUT - elif [ "$LAST_SHA" = "$CURRENT_SHA" ]; then - echo "No changes since last successful run" - echo "has_changes=false" >> $GITHUB_OUTPUT - else - echo "Changes detected since last successful run" - echo "has_changes=true" >> $GITHUB_OUTPUT - fi diff --git a/.github/workflows/ci.yml b/.github/workflows/pre-merge.yml similarity index 99% rename from .github/workflows/ci.yml rename to .github/workflows/pre-merge.yml index a04272630..75d9ca013 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/pre-merge.yml @@ -1,4 +1,4 @@ -name: CI +name: Pre-Merge on: pull_request: diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml deleted file mode 100644 index e358cfe8e..000000000 --- a/.github/workflows/qa.yml +++ /dev/null @@ -1,88 +0,0 @@ -name: QA Build - -on: - # Nightly build at 2am GMT/UTC - schedule: - - cron: '0 2 * * *' - # Allow manual trigger (always runs, skips the change check) - workflow_dispatch: - -jobs: - check-changes: - name: Check for changes - runs-on: ubuntu-latest - outputs: - has_changes: ${{ steps.manual.outputs.has_changes || steps.scheduled.outputs.has_changes }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - # For manual triggers, always run - - name: Manual trigger check - id: manual - if: github.event_name == 'workflow_dispatch' - run: | - echo "Manual trigger - will build" - echo "has_changes=true" >> $GITHUB_OUTPUT - - # For scheduled triggers, check if there are changes since last scheduled run - - name: Check for changes since last scheduled build - id: scheduled - if: github.event_name == 'schedule' - uses: ./.github/actions/get-last-successful-run - with: - workflow: qa.yml - event: schedule - - ios-staging: - name: iOS Staging (QA) - needs: check-changes - if: needs.check-changes.outputs.has_changes == 'true' - uses: ./.github/workflows/reusable-ios.yml - with: - environment: staging - scheme: Pera-Staging - notification_title_prefix: "QA Build (Nightly)" - use_cache: true - parent_workflow: qa.yml - secrets: inherit - - # ios-production: - # name: iOS Production (QA) - # needs: check-changes - # if: needs.check-changes.outputs.has_changes == 'true' - # uses: ./.github/workflows/reusable-ios.yml - # with: - # environment: production - # scheme: Pera-Production - # notification_title_prefix: "QA Build (Nightly)" - # use_cache: true - # parent_workflow: qa.yml - # secrets: inherit - - android-staging: - name: Android Staging (QA) - needs: check-changes - if: needs.check-changes.outputs.has_changes == 'true' - uses: ./.github/workflows/reusable-android.yml - with: - environment: staging - flavor: staging - notification_title_prefix: "QA Build (Nightly)" - use_cache: true - parent_workflow: qa.yml - distribution: firebase - secrets: inherit - - # android-production: - # name: Android Production (QA) - # needs: check-changes - # if: needs.check-changes.outputs.has_changes == 'true' - # uses: ./.github/workflows/reusable-android.yml - # with: - # environment: production - # flavor: production - # notification_title_prefix: "QA Build (Nightly)" - # use_cache: true - # parent_workflow: qa.yml - # secrets: inherit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index af53c72db..000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Release Build - -on: - push: - branches: - - 'release/**' - tags: - - 'v*' - workflow_dispatch: - -jobs: - test: - name: Test - runs-on: ubuntu-latest - environment: production - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version-file: '.tool-versions' - cache: 'pnpm' - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Config Generate - env: - PERA_MAINNET_BACKEND_URL: ${{ secrets.MAINNET_BACKEND_URL }} - PERA_TESTNET_BACKEND_URL: ${{ secrets.TESTNET_BACKEND_URL }} - PERA_BACKEND_API_KEY: ${{ secrets.BACKEND_API_KEY }} - PERA_ALGOD_API_KEY: ${{ secrets.ALGOD_API_KEY }} - PERA_INDEXER_API_KEY: ${{ secrets.INDEXER_API_KEY }} - PERA_MAINNET_ALGOD_URL: ${{ secrets.MAINNET_ALGOD_URL }} - PERA_TESTNET_ALGOD_URL: ${{ secrets.TESTNET_ALGOD_URL }} - PERA_MAINNET_INDEXER_URL: ${{ secrets.MAINNET_INDEXER_URL }} - PERA_TESTNET_INDEXER_URL: ${{ secrets.TESTNET_INDEXER_URL }} - run: pnpm run build:packages --filter=@perawallet/wallet-core-config - - - name: Test - run: pnpm run test - - ios-production: - name: iOS Production (RC) - needs: test - uses: ./.github/workflows/reusable-ios.yml - with: - environment: production - scheme: Pera-Production - notification_title_prefix: "Release Candidate" - use_cache: false - parent_workflow: release.yml - secrets: inherit - - android-production: - name: Android Production (RC) - needs: test - uses: ./.github/workflows/reusable-android.yml - with: - environment: production - flavor: production - notification_title_prefix: "Release Candidate" - use_cache: false - parent_workflow: release.yml - distribution: playstore - secrets: inherit diff --git a/.github/workflows/reusable-android.yml b/.github/workflows/reusable-android.yml deleted file mode 100644 index 93f5b3022..000000000 --- a/.github/workflows/reusable-android.yml +++ /dev/null @@ -1,254 +0,0 @@ -name: Reusable Android Build - -on: - workflow_call: - inputs: - environment: - required: true - type: string - flavor: - required: true - type: string - notification_title_prefix: - required: true - type: string - use_cache: - required: false - type: boolean - default: false - parent_workflow: - required: true - type: string - description: 'The parent workflow file name (e.g., qa.yml or release.yml) for changelog generation' - distribution: - required: false - type: string - default: 'firebase' - description: 'Distribution method: firebase (App Distribution) or playstore (Internal track)' - secrets: - MAINNET_BACKEND_URL: - required: true - TESTNET_BACKEND_URL: - required: true - BACKEND_API_KEY: - required: true - ALGOD_API_KEY: - required: true - INDEXER_API_KEY: - required: true - MAINNET_ALGOD_URL: - required: true - TESTNET_ALGOD_URL: - required: true - MAINNET_INDEXER_URL: - required: true - TESTNET_INDEXER_URL: - required: true - ANDROID_GOOGLE_SERVICES_BASE64: - required: true - ANDROID_KEYSTORE_BASE64: - required: true - ANDROID_KEYSTORE_PASSWORD: - required: true - ANDROID_KEY_ALIAS: - required: true - ANDROID_KEY_PASSWORD: - required: true - ANDROID_JSON_KEY_FILE: - required: true - FIREBASE_APP_ID_ANDROID: - required: false - FIREBASE_SERVICE_ACCOUNT_BASE64: - required: false - FIREBASE_TESTER_GROUPS: - required: false - JIRA_BASE_URL: - required: true - JIRA_USER_EMAIL: - required: true - JIRA_API_TOKEN: - required: true - SLACK_WEBHOOK_URL: - required: true - -jobs: - build: - name: Build & Deploy - runs-on: ubuntu-latest-m - outputs: - version: ${{ steps.set_outputs.outputs.version }} - build_number: ${{ steps.set_outputs.outputs.build_number }} - changelog: ${{ steps.changelog.outputs.changelog }} - environment: ${{ inputs.environment }} - timeout-minutes: 120 - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - fetch-tags: true - - - name: Generate Changelog - id: changelog - uses: ./.github/actions/generate-changelog - with: - workflow: ${{ inputs.parent_workflow }} - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version-file: '.tool-versions' - cache: 'pnpm' - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Config Generate - env: - PERA_MAINNET_BACKEND_URL: ${{ secrets.MAINNET_BACKEND_URL }} - PERA_TESTNET_BACKEND_URL: ${{ secrets.TESTNET_BACKEND_URL }} - PERA_BACKEND_API_KEY: ${{ secrets.BACKEND_API_KEY }} - PERA_ALGOD_API_KEY: ${{ secrets.ALGOD_API_KEY }} - PERA_INDEXER_API_KEY: ${{ secrets.INDEXER_API_KEY }} - PERA_MAINNET_ALGOD_URL: ${{ secrets.MAINNET_ALGOD_URL }} - PERA_TESTNET_ALGOD_URL: ${{ secrets.TESTNET_ALGOD_URL }} - PERA_MAINNET_INDEXER_URL: ${{ secrets.MAINNET_INDEXER_URL }} - PERA_TESTNET_INDEXER_URL: ${{ secrets.TESTNET_INDEXER_URL }} - run: pnpm run build:packages --filter=@perawallet/wallet-core-config - - - name: Inject Google Services JSON - env: - ANDROID_GOOGLE_SERVICES_BASE64: ${{ secrets.ANDROID_GOOGLE_SERVICES_BASE64 }} - run: | - echo "$ANDROID_GOOGLE_SERVICES_BASE64" | base64 --decode > apps/mobile/android/app/google-services.json - - - name: Setup JDK 17 - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version-file: '.tool-versions' - - - name: Cache Gradle - uses: actions/cache@v4 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: android-gradle-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml', 'apps/mobile/android/**/*.gradle*', 'apps/mobile/android/**/gradle-wrapper.properties') }} - restore-keys: | - android-gradle-${{ runner.os }}- - - - - name: Install Ccache - run: | - sudo apt-get update - sudo apt-get install -y ccache - mkdir -p $HOME/.ccache - echo "NDK_CCACHE=ccache" >> $GITHUB_ENV - echo "CMAKE_C_COMPILER_LAUNCHER=ccache" >> $GITHUB_ENV - echo "CMAKE_CXX_COMPILER_LAUNCHER=ccache" >> $GITHUB_ENV - - - name: Cache Ccache - uses: actions/cache@v4 - with: - path: ~/.ccache - key: android-ccache-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml', 'apps/mobile/android/**/*.gradle*', 'apps/mobile/android/**/gradle-wrapper.properties') }} - restore-keys: | - android-ccache-${{ runner.os }}- - - - name: Setup Android SDK - uses: android-actions/setup-android@v3 - with: - accept-android-sdk-licenses: true - log-accepted-android-sdk-licenses: false - - - name: Setup Ruby - env: - BUNDLE_GEMFILE: apps/mobile/Gemfile - uses: ruby/setup-ruby@v1 - with: - ruby-version: '3.2' - bundler-cache: true - - - name: Decode Android keystore - env: - ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} - run: | - echo "$ANDROID_KEYSTORE_BASE64" | base64 --decode > apps/mobile/android/app/release.keystore - - - name: Create keystore.properties - env: - ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} - ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} - ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} - run: | - cat > apps/mobile/android/keystore.properties << EOF - storeFile=release.keystore - storePassword=$ANDROID_KEYSTORE_PASSWORD - keyAlias=$ANDROID_KEY_ALIAS - keyPassword=$ANDROID_KEY_PASSWORD - EOF - - - name: Create local.properties - run: echo "sdk.dir=$ANDROID_HOME" > apps/mobile/android/local.properties - - - name: Get version from package.json - run: echo "VERSION=$(jq -r '.version | split("-")[0]' apps/mobile/package.json)" >> $GITHUB_ENV - - - name: Setup Firebase credentials - if: inputs.distribution == 'firebase' - env: - FIREBASE_SERVICE_ACCOUNT_BASE64: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_BASE64 }} - run: | - echo "$FIREBASE_SERVICE_ACCOUNT_BASE64" | base64 --decode > apps/mobile/android/firebase-service-account.json - - - name: Setup Play Store credentials - if: inputs.distribution == 'playstore' - env: - ANDROID_JSON_KEY_FILE: ${{ secrets.ANDROID_JSON_KEY_FILE }} - run: | - echo "$ANDROID_JSON_KEY_FILE" > apps/mobile/android/api-key.json - - - name: Build & Deploy Android - env: - BUILD_NUMBER: ${{ github.run_number }} - FIREBASE_APP_ID_ANDROID: ${{ secrets.FIREBASE_APP_ID_ANDROID }} - FIREBASE_TESTER_GROUPS: ${{ secrets.FIREBASE_TESTER_GROUPS }} - RELEASE_NOTES: ${{ steps.changelog.outputs.changelog }} - working-directory: apps/mobile - run: | - # Use clean=true if caching is NOT enabled - CLEAN="true" - if [ "${{ inputs.use_cache }}" == "true" ]; then - CLEAN="false" - fi - - if [ "${{ inputs.distribution }}" == "firebase" ]; then - bundle exec fastlane android deploy_firebase flavor:${{ inputs.flavor }} clean:$CLEAN - else - bundle exec fastlane android deploy_internal flavor:${{ inputs.flavor }} clean:$CLEAN - fi - - - name: Set outputs - if: always() - run: | - echo "version=${{ env.VERSION }}" >> $GITHUB_OUTPUT - echo "build_number=${{ github.run_number }}" >> $GITHUB_OUTPUT - id: set_outputs - notify: - needs: build - if: always() - uses: ./.github/workflows/reusable-slack-notify.yml - with: - status: ${{ needs.build.result }} - platform: "Android" - environment: ${{ inputs.environment }} - # Check if outputs are available, fallback to 'N/A' if empty (e.g. build failed early) - version: ${{ needs.build.outputs.version || 'N/A' }} - build_number: ${{ needs.build.outputs.build_number || 'N/A' }} - notification_title_prefix: ${{ inputs.notification_title_prefix }} - changelog: ${{ needs.build.outputs.changelog || 'Unable to generate changelog' }} - secrets: inherit diff --git a/.github/workflows/reusable-ios.yml b/.github/workflows/reusable-ios.yml deleted file mode 100644 index 5e18b1c21..000000000 --- a/.github/workflows/reusable-ios.yml +++ /dev/null @@ -1,240 +0,0 @@ -name: Reusable iOS Build - -on: - workflow_call: - inputs: - environment: - required: true - type: string - scheme: - required: true - type: string - notification_title_prefix: - required: true - type: string - use_cache: - required: false - type: boolean - default: false - parent_workflow: - required: true - type: string - description: 'The parent workflow file name (e.g., qa.yml or release.yml) for changelog generation' - secrets: - # We use 'secrets: inherit' in the caller, but we can list required ones here for documentation or specific passing - MAINNET_BACKEND_URL: - required: true - TESTNET_BACKEND_URL: - required: true - BACKEND_API_KEY: - required: true - ALGOD_API_KEY: - required: true - INDEXER_API_KEY: - required: true - MAINNET_ALGOD_URL: - required: true - TESTNET_ALGOD_URL: - required: true - MAINNET_INDEXER_URL: - required: true - TESTNET_INDEXER_URL: - required: true - IOS_GOOGLE_SERVICE_INFO_BASE64: - required: true - IOS_DISTRIBUTION_CERT_BASE64: - required: true - IOS_DISTRIBUTION_CERT_PASSWORD: - required: true - IOS_PROVISIONING_PROFILE_BASE64: - required: true - APP_STORE_CONNECT_API_KEY_KEY_ID: - required: true - APP_STORE_CONNECT_API_KEY_ISSUER_ID: - required: true - APP_STORE_CONNECT_API_KEY_CONTENT_BASE64: - required: true - JIRA_BASE_URL: - required: true - JIRA_USER_EMAIL: - required: true - JIRA_API_TOKEN: - required: true - SLACK_WEBHOOK_URL: - required: true - -jobs: - build: - name: Build & Deploy - runs-on: macos-26 - outputs: - version: ${{ steps.set_outputs.outputs.version }} - build_number: ${{ steps.set_outputs.outputs.build_number }} - changelog: ${{ steps.changelog.outputs.changelog }} - environment: ${{ inputs.environment }} - timeout-minutes: 120 - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - fetch-tags: true - - - name: Generate Changelog - id: changelog - uses: ./.github/actions/generate-changelog - with: - workflow: ${{ inputs.parent_workflow }} - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version-file: '.tool-versions' - cache: 'pnpm' - - - name: Cache node_modules - uses: actions/cache@v4 - with: - path: | - node_modules - apps/*/node_modules - packages/*/node_modules - key: node-modules-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} - - - name: Install dependencies - run: pnpm install --frozen-lockfile --prefer-offline - - - name: Config Generate - env: - PERA_MAINNET_BACKEND_URL: ${{ secrets.MAINNET_BACKEND_URL }} - PERA_TESTNET_BACKEND_URL: ${{ secrets.TESTNET_BACKEND_URL }} - PERA_BACKEND_API_KEY: ${{ secrets.BACKEND_API_KEY }} - PERA_ALGOD_API_KEY: ${{ secrets.ALGOD_API_KEY }} - PERA_INDEXER_API_KEY: ${{ secrets.INDEXER_API_KEY }} - PERA_MAINNET_ALGOD_URL: ${{ secrets.MAINNET_ALGOD_URL }} - PERA_TESTNET_ALGOD_URL: ${{ secrets.TESTNET_ALGOD_URL }} - PERA_MAINNET_INDEXER_URL: ${{ secrets.MAINNET_INDEXER_URL }} - PERA_TESTNET_INDEXER_URL: ${{ secrets.TESTNET_INDEXER_URL }} - run: pnpm run build:packages --filter=@perawallet/wallet-core-config - - - name: Inject Google Service Info - env: - IOS_GOOGLE_SERVICE_INFO_BASE64: ${{ secrets.IOS_GOOGLE_SERVICE_INFO_BASE64 }} - run: | - echo "$IOS_GOOGLE_SERVICE_INFO_BASE64" | base64 --decode > apps/mobile/ios/GoogleService-Info.plist - - - name: Setup Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: '3.2' - bundler-cache: true - working-directory: apps/mobile - - - name: Cache CocoaPods - id: pods-cache - uses: actions/cache@v4 - with: - path: apps/mobile/ios/Pods - key: ios-pods-${{ runner.os }}-${{ hashFiles('apps/mobile/ios/Podfile.lock') }} - restore-keys: | - ios-pods-${{ runner.os }}- - - - name: Install CocoaPods - run: | - cd apps/mobile/ios - bundle exec pod install - - - - name: Cache Derived Data - if: ${{ inputs.use_cache }} - uses: actions/cache@v4 - with: - path: ~/Library/Developer/Xcode/DerivedData - key: ios-derived-data-${{ runner.os }}-${{ inputs.scheme }}-${{ hashFiles('apps/mobile/ios/Podfile.lock', 'pnpm-lock.yaml') }} - restore-keys: | - ios-derived-data-${{ runner.os }}-${{ inputs.scheme }}- - ios-derived-data-${{ runner.os }}- - - - name: Setup MacOS Keychain & Signing - env: - CERT_BASE64: ${{ secrets.IOS_DISTRIBUTION_CERT_BASE64 }} - CERT_PASSWORD: ${{ secrets.IOS_DISTRIBUTION_CERT_PASSWORD }} - PROFILE_BASE64: ${{ secrets.IOS_PROVISIONING_PROFILE_BASE64 }} - run: | - KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db - KEYCHAIN_PASSWORD="actions" - - # Re-create keychain - security delete-keychain $KEYCHAIN_PATH || true - security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH - security set-keychain-settings -lut 21600 $KEYCHAIN_PATH - security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH - - # Import Certificate - echo "$CERT_BASE64" | base64 --decode > certificate.p12 - security import certificate.p12 -P "$CERT_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH - security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH - - # Install Profile (must be named with UUID for Xcode to find it) - mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles - echo "$PROFILE_BASE64" | base64 --decode > profile.mobileprovision - - # Extract UUID and Name - PROFILE_CONTENT=$(security cms -D -i profile.mobileprovision) - PROFILE_UUID=$(/usr/libexec/PlistBuddy -c "Print UUID" /dev/stdin <<< "$PROFILE_CONTENT") - PROFILE_NAME=$(/usr/libexec/PlistBuddy -c "Print Name" /dev/stdin <<< "$PROFILE_CONTENT") - - cp profile.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/${PROFILE_UUID}.mobileprovision - echo "Installed profile with UUID: ${PROFILE_UUID}" - echo "Profile Name: ${PROFILE_NAME}" - echo "IOS_PROFILE_NAME=${PROFILE_NAME}" >> $GITHUB_ENV - - # Set keychain as default search list - security list-keychain -d user -s $KEYCHAIN_PATH - - - name: Get version from package.json - run: echo "VERSION=$(jq -r '.version | split("-")[0]' apps/mobile/package.json)" >> $GITHUB_ENV - - - name: Build & Deploy iOS - id: build - env: - APP_STORE_CONNECT_API_KEY_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY_ID }} - APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }} - BUILD_NUMBER: ${{ github.run_number }} - working-directory: apps/mobile - run: | - # Decode API Key - echo "${{ secrets.APP_STORE_CONNECT_API_KEY_CONTENT_BASE64 }}" | base64 --decode > api_key.p8 - export APP_STORE_CONNECT_API_KEY_CONTENT=$(cat api_key.p8) - - # Use clean=true if caching is NOT enabled - CLEAN="true" - if [ "${{ inputs.use_cache }}" == "true" ]; then - CLEAN="false" - fi - - bundle exec fastlane ios deploy_testflight scheme:${{ inputs.scheme }} clean:$CLEAN - - - name: Set outputs - if: always() - run: | - echo "version=${{ env.VERSION }}" >> $GITHUB_OUTPUT - echo "build_number=${{ github.run_number }}" >> $GITHUB_OUTPUT - id: set_outputs - notify: - needs: build - if: always() - uses: ./.github/workflows/reusable-slack-notify.yml - with: - status: ${{ needs.build.result }} - platform: "iOS" - environment: ${{ inputs.environment }} - # Check if outputs are available, fallback to 'N/A' if empty (e.g. build failed early) - version: ${{ needs.build.outputs.version || 'N/A' }} - build_number: ${{ needs.build.outputs.build_number || 'N/A' }} - notification_title_prefix: ${{ inputs.notification_title_prefix }} - changelog: ${{ needs.build.outputs.changelog || 'Unable to generate changelog' }} - secrets: inherit diff --git a/.github/workflows/reusable-slack-notify.yml b/.github/workflows/reusable-slack-notify.yml deleted file mode 100644 index e3b45cb81..000000000 --- a/.github/workflows/reusable-slack-notify.yml +++ /dev/null @@ -1,122 +0,0 @@ -name: Reusable Slack Notification - -on: - workflow_call: - inputs: - status: - required: true - type: string - description: 'Job status (success, failure, cancelled)' - platform: - required: true - type: string - description: 'Platform name (iOS, Android)' - environment: - required: true - type: string - description: 'Environment (staging, production)' - version: - required: false - type: string - default: 'N/A' - build_number: - required: false - type: string - default: 'N/A' - notification_title_prefix: - required: true - type: string - changelog: - required: false - type: string - default: 'No changes detected' - description: 'Changelog since last build' - - secrets: - SLACK_WEBHOOK_URL: - required: true - JIRA_BASE_URL: - required: true - -jobs: - notify: - name: Slack Notification - runs-on: ubuntu-latest - environment: ${{ inputs.environment }} - steps: - - name: Send Slack Notification - uses: rtCamp/action-slack-notify@v2 - env: - SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} - SLACK_CUSTOM_PAYLOAD: | - { - "username": "GitHub Actions", - "icon_emoji": ":octocat:", - "blocks": [ - { - "type": "header", - "text": { - "type": "plain_text", - "text": "${{ inputs.status == 'success' && '✅' || '❌' }} RN ${{ inputs.notification_title_prefix }}: ${{ inputs.platform }} ${{ inputs.environment }}${{ inputs.status == 'success' && ' Available on TestFlight/Play Store' || ' Failed' }}", - "emoji": true - } - } - ], - "attachments": [ - { - "color": "${{ inputs.status == 'success' && '#36a64f' || '#ec0000' }}", - "blocks": [ - { - "type": "section", - "fields": [ - { - "type": "mrkdwn", - "text": "*Platform:*\n${{ inputs.platform }}" - }, - { - "type": "mrkdwn", - "text": "*Version:*\n${{ inputs.version }} (${{ inputs.build_number }})" - }, - { - "type": "mrkdwn", - "text": "*Status:*\n${{ inputs.status }}" - } - ] - }, - { - "type": "divider" - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "*Changes since last build:*" - } - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "${{ inputs.changelog }}" - } - }, - { - "type": "divider" - }, - { - "type": "context", - "elements": [ - { - "type": "mrkdwn", - "text": "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Action Run>" - }, - { - "type": "mrkdwn", - "text": "" - } - ] - } - ] - } - ] - } diff --git a/.tool-versions b/.tool-versions index 4512857f6..141b06857 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,3 +1,3 @@ ruby 3.2 nodejs 22 -java 17 +java temurin-17.0.17+10 diff --git a/tools/generate_changelog.sh b/tools/generate_changelog.sh index 5e7d7a08c..1ca14c4e9 100755 --- a/tools/generate_changelog.sh +++ b/tools/generate_changelog.sh @@ -1,18 +1,19 @@ #!/bin/bash if [ -z "$1" ]; then - echo "Usage: $0 " >&2 - exit 1 + # No SHA provided, try to find initial commit or just use HEAD~1 + LAST_SHA="" +else + LAST_SHA=$1 fi -LAST_SHA=$1 - if [ -n "$LAST_SHA" ] && git cat-file -t "$LAST_SHA" >/dev/null 2>&1; then echo "Found last successful build at commit: $LAST_SHA" >&2 LAST_REF="$LAST_SHA" else - echo "No previous successful build found, using initial commit" >&2 - LAST_REF=$(git rev-list --max-parents=0 HEAD) + echo "No previous successful build found (or invalid SHA), using initial commit" >&2 + # Fallback to the first commit if possible, or just log from the beginning + LAST_REF=$(git rev-list --max-parents=0 HEAD | head -n 1) fi echo "Generating changelog from $LAST_REF to HEAD" >&2