From e71bdc17ae566dc4df2f429a0c5ef52861f0afd4 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Thu, 5 Feb 2026 01:05:55 +0530 Subject: [PATCH 1/7] feat: enhance macOS and Android CI workflows for app packaging - Updated the package-and-publish.yml to improve macOS app re-signing by adding certificate handling and keychain management. - Modified package-android.yml to transition from APK to AAB packaging, including updates to artifact handling and Google Play upload. - Enhanced the bundle-tdlib-macos.sh script to fix binary rpaths, ensuring proper runtime behavior of bundled frameworks. --- .github/workflows/package-and-publish.yml | 35 ++++++++-- .github/workflows/package-android.yml | 82 +++++++++++------------ src-tauri/scripts/bundle-tdlib-macos.sh | 25 +++++-- 3 files changed, 93 insertions(+), 49 deletions(-) diff --git a/.github/workflows/package-and-publish.yml b/.github/workflows/package-and-publish.yml index 201d52a31..ff50444f9 100644 --- a/.github/workflows/package-and-publish.yml +++ b/.github/workflows/package-and-publish.yml @@ -340,16 +340,37 @@ jobs: - name: Re-sign macOS app after TDLib bundling if: matrix.settings.platform == 'macos-latest' env: + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE_BASE64 }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} run: | TARGET="${{ matrix.settings.target }}" echo "Re-signing for target: $TARGET" - if [ -z "$APPLE_SIGNING_IDENTITY" ]; then - echo "No signing identity provided, skipping re-sign" + if [ -z "$APPLE_SIGNING_IDENTITY" ] || [ -z "$APPLE_CERTIFICATE" ]; then + echo "No signing identity or certificate provided, skipping re-sign" exit 0 fi + # Import certificate into a temporary keychain (same approach as tauri-action) + KEYCHAIN_PATH="$RUNNER_TEMP/tdlib-signing.keychain-db" + KEYCHAIN_PASSWORD=$(openssl rand -base64 32) + + # Create and configure keychain + 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 + CERT_PATH="$RUNNER_TEMP/certificate.p12" + echo "$APPLE_CERTIFICATE" | base64 --decode > "$CERT_PATH" + security import "$CERT_PATH" -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH" + rm "$CERT_PATH" + + # Add keychain to search list + security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"') + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + # Find app bundle in target-specific or default location if [ -n "$TARGET" ]; then APP_BUNDLE=$(find "src-tauri/target/$TARGET/release/bundle/macos" -name "*.app" -type d 2>/dev/null | head -1) @@ -364,16 +385,22 @@ jobs: for dylib in "$APP_BUNDLE/Contents/Frameworks/"*.dylib; do if [ -f "$dylib" ]; then echo "Signing: $dylib" - codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" "$dylib" || true + codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" "$dylib" fi done # Re-sign the entire app bundle - codesign --force --deep --options runtime --sign "$APPLE_SIGNING_IDENTITY" "$APP_BUNDLE" || true + codesign --force --deep --options runtime --sign "$APPLE_SIGNING_IDENTITY" "$APP_BUNDLE" echo "App re-signed successfully" + + # Verify signature + codesign --verify --verbose=2 "$APP_BUNDLE" || echo "Warning: Signature verification had issues" else echo "Warning: No app bundle found to re-sign" fi + # Cleanup keychain + security delete-keychain "$KEYCHAIN_PATH" || true + # Re-create DMG after TDLib bundling (the original DMG was created before bundling) - name: Recreate DMG with bundled TDLib if: matrix.settings.platform == 'macos-latest' diff --git a/.github/workflows/package-android.yml b/.github/workflows/package-android.yml index 53ade7a33..82be4000c 100644 --- a/.github/workflows/package-android.yml +++ b/.github/workflows/package-android.yml @@ -87,14 +87,14 @@ jobs: if [ "$HTTP_CODE" = "200" ]; then RELEASE_ID=$(echo "$BODY" | jq -r '.id') IS_DRAFT=$(echo "$BODY" | jq -r '.draft') - # Check if Android APK is already uploaded - APK_EXISTS=$(echo "$BODY" | jq -r '.assets[]? | select(.name | endswith(".apk")) | .name' | head -1) - if [ -n "$APK_EXISTS" ]; then - echo "Android APK already exists in release: $APK_EXISTS" + # Check if Android AAB is already uploaded + AAB_EXISTS=$(echo "$BODY" | jq -r '.assets[]? | select(.name | endswith(".aab")) | .name' | head -1) + if [ -n "$AAB_EXISTS" ]; then + echo "Android AAB already exists in release: $AAB_EXISTS" echo "should-skip=true" >> $GITHUB_OUTPUT echo "release-id=$RELEASE_ID" >> $GITHUB_OUTPUT else - echo "Release found (ID: $RELEASE_ID, draft: $IS_DRAFT), no APK yet" + echo "Release found (ID: $RELEASE_ID, draft: $IS_DRAFT), no AAB yet" echo "should-skip=false" >> $GITHUB_OUTPUT echo "release-id=$RELEASE_ID" >> $GITHUB_OUTPUT fi @@ -108,7 +108,7 @@ jobs: echo "release-id=" >> $GITHUB_OUTPUT fi build-android: - name: Build and package Android APK + name: Build and package Android AAB needs: [get-version, check-release] if: ${{ !failure() && !cancelled() && needs.check-release.outputs.should-skip != 'true' }} environment: ${{ github.ref == 'refs/heads/main' && 'Production' || '' }} @@ -191,7 +191,7 @@ jobs: ANDROID_KEYSTORE: ${{ secrets.ANDROID_KEYSTORE }} run: | echo "$ANDROID_KEYSTORE" | base64 --decode > ${{ github.workspace }}/release.jks - - name: Build Android APK (signed) + - name: Build Android AAB (signed) if: needs.get-version.outputs.should-publish != '' env: BASE_URL: ${{ vars.BASE_URL }} @@ -199,54 +199,54 @@ jobs: ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} - run: npx tauri android build --apk true + run: npx tauri android build --aab true - - name: Build Android APK (unsigned) + - name: Build Android AAB (unsigned) if: needs.get-version.outputs.should-publish == '' env: BASE_URL: ${{ vars.BASE_URL }} - run: npx tauri android build --apk true + run: npx tauri android build --aab true - - name: Find APK - id: find-apk + - name: Find AAB + id: find-aab run: | - APK_ROOT="src-tauri/gen/android" - echo "Searching for APKs in $APK_ROOT..." - find "$APK_ROOT" -name '*.apk' -type f | sort - # Prefer universal release, then any signed release, then any APK (excluding unsigned) - APK_PATH=$(find "$APK_ROOT" -name '*-universal-release.apk' -type f | head -1) - if [ -z "$APK_PATH" ]; then - APK_PATH=$(find "$APK_ROOT" -name '*-release.apk' -not -name '*-unsigned.apk' -type f | head -1) - fi - if [ -z "$APK_PATH" ]; then - APK_PATH=$(find "$APK_ROOT" -name '*-release*.apk' -not -name '*-unsigned.apk' -type f | head -1) - fi - if [ -z "$APK_PATH" ]; then - APK_PATH=$(find "$APK_ROOT" -name '*.apk' -not -name '*-unsigned.apk' -type f | head -1) - fi - if [ -z "$APK_PATH" ]; then - APK_PATH=$(find "$APK_ROOT" -name '*.apk' -type f | head -1) - fi - if [ -z "$APK_PATH" ]; then - echo "No APK found" + AAB_ROOT="src-tauri/gen/android" + echo "Searching for AABs in $AAB_ROOT..." + find "$AAB_ROOT" -name '*.aab' -type f | sort + AAB_PATH=$(find "$AAB_ROOT" -name '*.aab' -type f | head -1) + if [ -z "$AAB_PATH" ]; then + echo "No AAB found" exit 1 fi - APK_FILENAME=$(basename "$APK_PATH") - echo "Selected APK: $APK_PATH" - echo "path=$APK_PATH" >> $GITHUB_OUTPUT - echo "filename=$APK_FILENAME" >> $GITHUB_OUTPUT - - name: Upload APK artifact + AAB_FILENAME=$(basename "$AAB_PATH") + echo "Selected AAB: $AAB_PATH" + echo "path=$AAB_PATH" >> $GITHUB_OUTPUT + echo "filename=$AAB_FILENAME" >> $GITHUB_OUTPUT + - name: Upload AAB artifact uses: actions/upload-artifact@v4 with: - name: android-apk - path: ${{ steps.find-apk.outputs.path }} + name: android-aab + path: ${{ steps.find-aab.outputs.path }} - - name: Upload APK to release + - name: Upload AAB to release if: needs.get-version.outputs.should-publish == 'true' && needs.check-release.outputs.release-id != '' env: RELEASE_ID: ${{ needs.check-release.outputs.release-id }} run: | curl -X POST -H "Authorization: Bearer $XGH_TOKEN" \ -H "Content-Type: application/octet-stream" \ - --data-binary "@${{ steps.find-apk.outputs.path }}" \ - "https://uploads.github.com/repos/$PUBLISH_REPO/releases/$RELEASE_ID/assets?name=${{ steps.find-apk.outputs.filename }}" + --data-binary "@${{ steps.find-aab.outputs.path }}" \ + "https://uploads.github.com/repos/$PUBLISH_REPO/releases/$RELEASE_ID/assets?name=${{ steps.find-aab.outputs.filename }}" + + # Requires secret GOOGLE_PLAY_SERVICE_ACCOUNT_JSON (full JSON key from Play Console API access) + # Track: internal | alpha | beta | production (use vars.GOOGLE_PLAY_TRACK to override) + - name: Upload AAB to Google Play + if: needs.get-version.outputs.should-publish == 'true' && secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON != '' + uses: r0adkll/upload-google-play@v1 + with: + serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }} + packageName: com.alphahuman.app + releaseFiles: ${{ steps.find-aab.outputs.path }} + track: ${{ vars.GOOGLE_PLAY_TRACK || 'internal' }} + releaseName: AlphaHuman v${{ needs.get-version.outputs.package-version }} + status: completed diff --git a/src-tauri/scripts/bundle-tdlib-macos.sh b/src-tauri/scripts/bundle-tdlib-macos.sh index f04f1b7e1..99d5ac53c 100755 --- a/src-tauri/scripts/bundle-tdlib-macos.sh +++ b/src-tauri/scripts/bundle-tdlib-macos.sh @@ -184,13 +184,30 @@ for dylib in "${FRAMEWORKS_DIR}"/*.dylib; do fi done -# Add rpath to the binary to look in Frameworks +# Fix rpaths in the binary echo "" -echo "Adding rpath to binary..." -# First, try to delete existing rpath if it exists (ignore errors) +echo "Fixing binary rpaths..." + +# Get all current rpaths +CURRENT_RPATHS=$(otool -l "$BINARY_PATH" | grep -A2 "cmd LC_RPATH" | grep "path " | awk '{print $2}') + +echo "Current rpaths:" +echo "$CURRENT_RPATHS" + +# Remove all rpaths that point to build directories (they won't exist at runtime) +for rpath in $CURRENT_RPATHS; do + # Remove rpaths containing /build/, /target/, or absolute paths that aren't @executable_path + if [[ "$rpath" == *"/build/"* ]] || [[ "$rpath" == *"/target/"* ]] || [[ "$rpath" != @* ]]; then + echo "Removing build-time rpath: $rpath" + install_name_tool -delete_rpath "$rpath" "$BINARY_PATH" 2>/dev/null || true + fi +done + +# Delete our target rpath first if it exists (to avoid "already exists" error) install_name_tool -delete_rpath "@executable_path/../Frameworks" "$BINARY_PATH" 2>/dev/null || true -# Add the correct rpath +# Add the correct rpath for the bundled Frameworks +echo "Adding rpath: @executable_path/../Frameworks" install_name_tool -add_rpath "@executable_path/../Frameworks" "$BINARY_PATH" # Verify the changes From 90ac41d9e41f2eebe02071f6514bbe9a133ee29e Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Thu, 5 Feb 2026 01:07:08 +0530 Subject: [PATCH 2/7] feat: enhance Android CI workflow with skills module caching and build steps - Added support for caching node modules in the skills directory to improve build performance. - Introduced steps to install and build skills dependencies within the Android CI workflow. --- .github/workflows/package-android.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/package-android.yml b/.github/workflows/package-android.yml index 82be4000c..c5239515a 100644 --- a/.github/workflows/package-android.yml +++ b/.github/workflows/package-android.yml @@ -120,6 +120,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 1 + submodules: true ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref }} - name: Setup Node.js 24.x @@ -169,6 +170,22 @@ jobs: if: steps.yarn-cache.outputs.cache-hit != 'true' run: yarn install --frozen-lockfile + - name: Cache skills node modules + id: skills-yarn-cache + uses: actions/cache@v4 + with: + path: skills/node_modules + key: ${{ runner.os }}-skills-${{ hashFiles('skills/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-skills- + + - name: Install skills dependencies + if: steps.skills-yarn-cache.outputs.cache-hit != 'true' + run: cd skills && yarn install --frozen-lockfile + + - name: Build skills + run: cd skills && yarn build + - name: Build frontend run: yarn build env: From ffa9d99951360e4039e4f4e06f61b3429f869bca Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Thu, 5 Feb 2026 01:09:28 +0530 Subject: [PATCH 3/7] refactor: update Android CI workflow triggers and standardize quotes - Added push and pull request triggers for the develop branch in package-android.yml to enhance CI automation. - Standardized quote styles in the workflow configuration for consistency. --- .github/workflows/package-android.yml | 32 ++++++++++++++------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/.github/workflows/package-android.yml b/.github/workflows/package-android.yml index c5239515a..884c86079 100644 --- a/.github/workflows/package-android.yml +++ b/.github/workflows/package-android.yml @@ -4,23 +4,25 @@ name: Package Android on: - workflow_run: - workflows: ['Version Bump'] - types: - - completed - branches: - - main - - develop - pull_request: - branches: - - develop - - main workflow_dispatch: inputs: publish: - description: 'Publish to release (requires main branch)' + description: "Publish to release (requires main branch)" type: boolean default: false + push: + branches: + - develop + workflow_run: + workflows: ["Version Bump"] + types: + - completed + branches: + - main + pull_request: + branches: + - main + - develop env: IS_PR: ${{ github.event_name == 'pull_request' }} @@ -127,7 +129,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: 24.x - cache: 'yarn' + cache: "yarn" - name: Install Rust stable uses: dtolnay/rust-toolchain@stable @@ -137,8 +139,8 @@ jobs: - name: Setup Java 17 uses: actions/setup-java@v4 with: - distribution: 'temurin' - java-version: '17' + distribution: "temurin" + java-version: "17" - name: Setup Android SDK uses: android-actions/setup-android@v3 From 119084c45a403bd7987c9bbd4667cfec7642fb3e Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Thu, 5 Feb 2026 01:09:36 +0530 Subject: [PATCH 4/7] chore: standardize quote styles in package-android.yml - Updated quote styles in the package-android.yml workflow for consistency, changing double quotes to single quotes in descriptions and configuration options. --- .github/workflows/package-android.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/package-android.yml b/.github/workflows/package-android.yml index 884c86079..e15f01bdb 100644 --- a/.github/workflows/package-android.yml +++ b/.github/workflows/package-android.yml @@ -7,14 +7,14 @@ on: workflow_dispatch: inputs: publish: - description: "Publish to release (requires main branch)" + description: 'Publish to release (requires main branch)' type: boolean default: false push: branches: - develop workflow_run: - workflows: ["Version Bump"] + workflows: ['Version Bump'] types: - completed branches: @@ -129,7 +129,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: 24.x - cache: "yarn" + cache: 'yarn' - name: Install Rust stable uses: dtolnay/rust-toolchain@stable @@ -139,8 +139,8 @@ jobs: - name: Setup Java 17 uses: actions/setup-java@v4 with: - distribution: "temurin" - java-version: "17" + distribution: 'temurin' + java-version: '17' - name: Setup Android SDK uses: android-actions/setup-android@v3 From 00301b3bd96d6fb0049853b9478951160e1182fc Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Thu, 5 Feb 2026 01:11:35 +0530 Subject: [PATCH 5/7] chore: unify quote styles in package-android.yml - Standardized quote styles in the package-android.yml workflow by converting single quotes to double quotes for consistency across descriptions and configuration options. --- .github/workflows/package-android.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/package-android.yml b/.github/workflows/package-android.yml index e15f01bdb..0c42405cd 100644 --- a/.github/workflows/package-android.yml +++ b/.github/workflows/package-android.yml @@ -7,14 +7,14 @@ on: workflow_dispatch: inputs: publish: - description: 'Publish to release (requires main branch)' + description: "Publish to release (requires main branch)" type: boolean default: false push: branches: - develop workflow_run: - workflows: ['Version Bump'] + workflows: ["Version Bump"] types: - completed branches: @@ -129,7 +129,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: 24.x - cache: 'yarn' + cache: "yarn" - name: Install Rust stable uses: dtolnay/rust-toolchain@stable @@ -139,8 +139,8 @@ jobs: - name: Setup Java 17 uses: actions/setup-java@v4 with: - distribution: 'temurin' - java-version: '17' + distribution: "temurin" + java-version: "17" - name: Setup Android SDK uses: android-actions/setup-android@v3 @@ -260,7 +260,7 @@ jobs: # Requires secret GOOGLE_PLAY_SERVICE_ACCOUNT_JSON (full JSON key from Play Console API access) # Track: internal | alpha | beta | production (use vars.GOOGLE_PLAY_TRACK to override) - name: Upload AAB to Google Play - if: needs.get-version.outputs.should-publish == 'true' && secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON != '' + if: needs.get-version.outputs.should-publish == 'true' && ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }} != '' uses: r0adkll/upload-google-play@v1 with: serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }} From 484a99ea8f834bf8395375cfd557cfa6c561f40b Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Thu, 5 Feb 2026 01:13:15 +0530 Subject: [PATCH 6/7] fix: correct conditional syntax in Google Play upload step - Fixed the conditional syntax in the package-android.yml workflow to ensure proper evaluation of the GOOGLE_PLAY_SERVICE_ACCOUNT_JSON secret before uploading the AAB to Google Play. --- .github/workflows/package-android.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/package-android.yml b/.github/workflows/package-android.yml index 0c42405cd..d2b1634a1 100644 --- a/.github/workflows/package-android.yml +++ b/.github/workflows/package-android.yml @@ -260,7 +260,7 @@ jobs: # Requires secret GOOGLE_PLAY_SERVICE_ACCOUNT_JSON (full JSON key from Play Console API access) # Track: internal | alpha | beta | production (use vars.GOOGLE_PLAY_TRACK to override) - name: Upload AAB to Google Play - if: needs.get-version.outputs.should-publish == 'true' && ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }} != '' + if: needs.get-version.outputs.should-publish == 'true' && ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON != '' }} uses: r0adkll/upload-google-play@v1 with: serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }} From 2f99dec73db5c2e300357088738233c95db31506 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Thu, 5 Feb 2026 01:14:59 +0530 Subject: [PATCH 7/7] fix: update Google Play upload condition in CI workflow - Modified the condition for uploading AAB files to Google Play in package-android.yml to check for the GOOGLE_PLAY_UPLOAD_ENABLED variable instead of the GOOGLE_PLAY_SERVICE_ACCOUNT_JSON secret, ensuring proper configuration for the upload process. --- .github/workflows/package-android.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/package-android.yml b/.github/workflows/package-android.yml index d2b1634a1..3885b3d22 100644 --- a/.github/workflows/package-android.yml +++ b/.github/workflows/package-android.yml @@ -257,10 +257,10 @@ jobs: --data-binary "@${{ steps.find-aab.outputs.path }}" \ "https://uploads.github.com/repos/$PUBLISH_REPO/releases/$RELEASE_ID/assets?name=${{ steps.find-aab.outputs.filename }}" - # Requires secret GOOGLE_PLAY_SERVICE_ACCOUNT_JSON (full JSON key from Play Console API access) + # Requires secret GOOGLE_PLAY_SERVICE_ACCOUNT_JSON and vars.GOOGLE_PLAY_UPLOAD_ENABLED = 'true' # Track: internal | alpha | beta | production (use vars.GOOGLE_PLAY_TRACK to override) - name: Upload AAB to Google Play - if: needs.get-version.outputs.should-publish == 'true' && ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON != '' }} + if: needs.get-version.outputs.should-publish == 'true' && vars.GOOGLE_PLAY_UPLOAD_ENABLED == 'true' uses: r0adkll/upload-google-play@v1 with: serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }}