Add internet search to chat (web_search + read_url talents, BYOK) #1410
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI Pipeline | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| push: | |
| branches: | |
| - main | |
| permissions: | |
| contents: read | |
| jobs: | |
| # Detect if changes are translation-only (src/locales/ files) | |
| detect-changes: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| translation_only: ${{ steps.check.outputs.translation_only }} | |
| steps: | |
| - name: Check out code | |
| uses: actions/checkout@v3 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check if translation-only | |
| id: check | |
| run: | | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD) | |
| else | |
| CHANGED=$(git diff --name-only HEAD~1) | |
| fi | |
| echo "Changed files:" | |
| echo "$CHANGED" | |
| # Check if ALL changed files are under src/locales/ | |
| TRANSLATION_ONLY="true" | |
| while IFS= read -r file; do | |
| if [ -z "$file" ]; then | |
| continue | |
| fi | |
| case "$file" in | |
| src/locales/*) ;; | |
| *) TRANSLATION_ONLY="false"; break ;; | |
| esac | |
| done <<< "$CHANGED" | |
| echo "translation_only=$TRANSLATION_ONLY" >> "$GITHUB_OUTPUT" | |
| echo "Translation only: $TRANSLATION_ONLY" | |
| # Job for linting, type checking, unit testing | |
| build-and-test: | |
| runs-on: ubuntu-latest | |
| steps: | |
| # Step 1: Check out the repository | |
| - name: Check out code | |
| uses: actions/checkout@v3 | |
| # Cache node_modules | |
| - name: Cache node_modules | |
| uses: actions/cache@v3 | |
| with: | |
| path: node_modules | |
| key: ${{ runner.os }}-node-${{ hashFiles('yarn.lock') }} | |
| # Step 2: Set up Node.js environment | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v3 | |
| with: | |
| node-version: '22.21.0' | |
| cache: 'yarn' | |
| - name: Install Yarn | |
| run: npm install -g yarn | |
| # Step 3: Install dependencies and iOS Pods | |
| - name: Install dependencies | |
| run: yarn install | |
| # Step 4: Validate l10n files | |
| - name: Validate l10n files | |
| run: node scripts/validate-l10n.js | |
| # Step 4b: Verify font registration (I8 — every fontFamily must | |
| # be bundled on iOS + Android + listed in Info.plist UIAppFonts). | |
| - name: Verify font registration | |
| run: node scripts/verify-fonts.js | |
| # Step 5: Run linters (ESLint) | |
| - name: Run ESLint | |
| run: yarn lint | |
| # Step 5: Run TypeScript type checks | |
| - name: Run TypeScript type check | |
| run: yarn typecheck | |
| # Step 6: Run unit tests (Jest) | |
| - name: Run unit tests | |
| run: yarn test --coverage | |
| # Job for Android build | |
| build-android: | |
| runs-on: ubuntu-latest | |
| needs: [build-and-test, detect-changes] | |
| if: needs.detect-changes.outputs.translation_only != 'true' | |
| steps: | |
| - name: Check out code | |
| uses: actions/checkout@v3 | |
| # Set up Node.js environment | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v3 | |
| with: | |
| node-version: '22.21.0' | |
| cache: 'yarn' | |
| # Cache node_modules | |
| - name: Cache node_modules | |
| uses: actions/cache@v3 | |
| with: | |
| path: node_modules | |
| key: ${{ runner.os }}-node-${{ hashFiles('yarn.lock') }} | |
| - name: Install Yarn | |
| run: npm install -g yarn | |
| - name: Install dependencies | |
| run: yarn install | |
| - name: Set up JDK 17 | |
| uses: actions/setup-java@v3 | |
| with: | |
| java-version: '17' | |
| distribution: 'temurin' | |
| cache: 'gradle' | |
| - name: Create dummy google-services.json for CI | |
| run: | | |
| cat > android/app/google-services.json << 'EOL' | |
| { | |
| "project_info": { | |
| "project_number": "000000000000", | |
| "project_id": "dummy-project-for-ci", | |
| "storage_bucket": "dummy-project-for-ci.appspot.com" | |
| }, | |
| "client": [{ | |
| "client_info": { | |
| "mobilesdk_app_id": "1:000000000000:android:0000000000000000", | |
| "android_client_info": { | |
| "package_name": "com.pocketpalai" | |
| } | |
| }, | |
| "api_key": [{ | |
| "current_key": "dummy-api-key-for-ci-builds" | |
| }] | |
| }] | |
| } | |
| EOL | |
| - name: Create dummy .env file for CI | |
| run: | | |
| cat > .env << 'EOL' | |
| FIREBASE_FUNCTIONS_URL=https://dummy-firebase-url.com | |
| # PalsHub/Supabase Configuration | |
| SUPABASE_URL=https://dummy-supabase-url.supabase.co | |
| SUPABASE_ANON_KEY=dummy-anon-key-for-ci-builds | |
| PALSHUB_API_BASE_URL=https://dummy-palshub-api.com | |
| # 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=dummy-ios-client-id.apps.googleusercontent.com | |
| GOOGLE_WEB_CLIENT_ID=dummy-web-client-id.apps.googleusercontent.com | |
| EOL | |
| - name: Create dummy release keystore for CI | |
| working-directory: android/app | |
| run: | | |
| # Generate a dummy keystore for CI builds | |
| keytool -genkeypair -v \ | |
| -storetype PKCS12 \ | |
| -keystore pocketpal-release-key.keystore \ | |
| -alias pocketpal_key_alias \ | |
| -keyalg RSA \ | |
| -keysize 2048 \ | |
| -validity 10000 \ | |
| -storepass dummy-ci-password \ | |
| -keypass dummy-ci-password \ | |
| -dname "CN=CI Build, OU=CI, O=PocketPal, L=CI, S=CI, C=US" | |
| - name: Build Android Release | |
| working-directory: android | |
| env: | |
| APP_RELEASE_STORE_PASSWORD: dummy-ci-password | |
| APP_RELEASE_KEY_PASSWORD: dummy-ci-password | |
| run: ./gradlew assembleProdRelease | |
| - name: Verify prod APK has no automation-bridge code (DCE sanity check) | |
| run: | | |
| set -euo pipefail | |
| APK=android/app/build/outputs/apk/prod/release/app-prod-release.apk | |
| echo "Checking bundle inside $APK for E2E markers..." | |
| WORK=$(mktemp -d) | |
| unzip -p "$APK" assets/index.android.bundle > "$WORK/bundle" || { | |
| echo "FAIL: could not extract assets/index.android.bundle from APK" | |
| exit 1 | |
| } | |
| MARKERS=( | |
| 'AUTOMATION_BRIDGE' | |
| 'memory-snapshot-label' | |
| 'memory-snapshot-result' | |
| 'BENCH_RUN_MATRIX' | |
| 'bench-runner-screen-status' | |
| ) | |
| FAIL=0 | |
| for m in "${MARKERS[@]}"; do | |
| if grep -q -F "$m" "$WORK/bundle"; then | |
| echo "FAIL: prod bundle contains automation marker: $m" | |
| FAIL=1 | |
| fi | |
| done | |
| if [ "$FAIL" -ne 0 ]; then | |
| echo "" | |
| echo "The automation bridge was not DCE-stripped from the prod build." | |
| echo "Check that __E2E__ is being replaced at build time by babel-plugin-transform-define." | |
| echo "See src/__automation__/README.md for the DCE contract." | |
| exit 1 | |
| fi | |
| echo "OK: prod bundle contains zero automation markers." | |
| - name: Upload Android APK | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: android-release-apk | |
| path: android/app/build/outputs/apk/prod/release/app-prod-release.apk | |
| # Job for iOS build | |
| build-ios: | |
| runs-on: macos-26 # Keep CI on the same Xcode major as the release workflow | |
| needs: [build-and-test, detect-changes] | |
| if: needs.detect-changes.outputs.translation_only != 'true' | |
| env: | |
| NODE_OPTIONS: '--max-old-space-size=8192' | |
| steps: | |
| - name: Check out code | |
| uses: actions/checkout@v3 | |
| # Set up Node.js environment | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v3 | |
| with: | |
| node-version: '22.21.0' | |
| cache: 'yarn' | |
| # Cache node_modules | |
| - name: Cache node_modules | |
| uses: actions/cache@v3 | |
| with: | |
| path: node_modules | |
| key: ${{ runner.os }}-node-${{ hashFiles('yarn.lock') }} | |
| - name: Install Yarn | |
| run: npm install -g yarn | |
| - name: Install dependencies | |
| run: yarn install | |
| - name: Create Env.xcconfig | |
| run: | | |
| cat > ios/Config/Env.xcconfig << 'EOL' | |
| // This file contains environment-specific build settings. | |
| GOOGLE_WEB_CLIENT_ID = dummy-web-client-id.apps.googleusercontent.com | |
| REVERSED_GOOGLE_IOS_CLIENT_ID = com.googleusercontent.apps.dummy-ios-client-id | |
| EOL | |
| - name: Create dummy .env file for CI | |
| run: | | |
| cat > .env << 'EOL' | |
| FIREBASE_FUNCTIONS_URL=https://dummy-firebase-url.com | |
| # PalsHub/Supabase Configuration | |
| SUPABASE_URL=https://dummy-supabase-url.supabase.co | |
| SUPABASE_ANON_KEY=dummy-anon-key-for-ci-builds | |
| PALSHUB_API_BASE_URL=https://dummy-palshub-api.com | |
| # 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=dummy-ios-client-id.apps.googleusercontent.com | |
| GOOGLE_WEB_CLIENT_ID=dummy-web-client-id.apps.googleusercontent.com | |
| EOL | |
| - name: Create dummy GoogleService-Info.plist for CI | |
| run: | | |
| cat > ios/GoogleService-Info.plist << 'EOL' | |
| <?xml version="1.0" encoding="UTF-8"?> | |
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
| <plist version="1.0"> | |
| <dict> | |
| <key>CLIENT_ID</key> | |
| <string>dummy-client-id.apps.googleusercontent.com</string> | |
| <key>REVERSED_CLIENT_ID</key> | |
| <string>com.googleusercontent.apps.dummy-client-id</string> | |
| <key>API_KEY</key> | |
| <string>dummy-api-key-for-ci-builds</string> | |
| <key>GCM_SENDER_ID</key> | |
| <string>000000000000</string> | |
| <key>PLIST_VERSION</key> | |
| <string>1</string> | |
| <key>BUNDLE_ID</key> | |
| <string>ai.pocketpal</string> | |
| <key>PROJECT_ID</key> | |
| <string>dummy-project-for-ci</string> | |
| <key>STORAGE_BUCKET</key> | |
| <string>dummy-project-for-ci.appspot.com</string> | |
| <key>IS_ADS_ENABLED</key> | |
| <false/> | |
| <key>IS_ANALYTICS_ENABLED</key> | |
| <false/> | |
| <key>IS_APPINVITE_ENABLED</key> | |
| <false/> | |
| <key>IS_GCM_ENABLED</key> | |
| <true/> | |
| <key>IS_SIGNIN_ENABLED</key> | |
| <true/> | |
| <key>GOOGLE_APP_ID</key> | |
| <string>1:000000000000:ios:0000000000000000</string> | |
| </dict> | |
| </plist> | |
| EOL | |
| - name: Install CocoaPods dependencies | |
| run: | | |
| cd ios | |
| pod install | |
| cd .. | |
| - name: Select default Xcode | |
| run: | | |
| sudo xcode-select -s /Applications/Xcode.app | |
| xcodebuild -version | |
| - name: Build iOS Release | |
| run: | | |
| cd ios | |
| # List available simulators for debugging | |
| xcrun simctl list devices available | |
| xcodebuild \ | |
| -workspace PocketPal.xcworkspace \ | |
| -scheme PocketPal \ | |
| -configuration Release \ | |
| -sdk iphonesimulator \ | |
| -destination 'generic/platform=iOS Simulator' \ | |
| CODE_SIGN_IDENTITY="" \ | |
| CODE_SIGNING_REQUIRED=NO \ | |
| CODE_SIGNING_ALLOWED=NO \ | |
| ONLY_ACTIVE_ARCH=NO \ | |
| 2>&1 | tee ../ios-build.log | |
| EXIT_CODE=${PIPESTATUS[0]} | |
| if [ $EXIT_CODE -ne 0 ]; then | |
| echo "::error::iOS build failed (exit $EXIT_CODE)" | |
| echo "::group::Error context" | |
| grep -B 5 -A 20 "error:" ../ios-build.log | tail -200 || tail -100 ../ios-build.log | |
| echo "::endgroup::" | |
| exit $EXIT_CODE | |
| fi |