New Crowdin updates #3316
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: Build CI | |
| on: | |
| push: | |
| branches: [ "develop" ] | |
| pull_request: | |
| branches: [ "develop" ] | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: set up JDK 21 | |
| uses: actions/setup-java@v5 | |
| with: | |
| java-version: '21' | |
| distribution: 'temurin' | |
| cache: gradle | |
| - name: Set up Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| - name: Grant execute permission for gradlew | |
| run: chmod +x gradlew | |
| - name: Build with Gradle | |
| run: ./gradlew buildDebug | |
| # Compiles + packages the Android instrumented (androidTest) source set without a device. | |
| # buildDebug does not touch androidTest, so stale instrumented tests rot silently otherwise. | |
| - name: Compile Android instrumented tests | |
| run: ./gradlew :android:assembleDebugAndroidTest | |
| - name: Run Kover | |
| run: ./gradlew koverXmlReport | |
| - name: Upload coverage reports to Codecov | |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| # Explicit gate for the server unit tests (incl. EntityHashSensitivityTest). These otherwise | |
| # only run as a side-effect of koverXmlReport, which is fragile - a kover config change would | |
| # silently stop running them. | |
| - name: Run Server Unit Tests | |
| id: server-tests | |
| run: ./gradlew :server:test | |
| - name: Upload server test report on failure | |
| if: failure() && steps.server-tests.outcome == 'failure' | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: server-tests-report | |
| path: | | |
| server/build/reports/tests/test/ | |
| server/build/test-results/test/ | |
| - name: Run JavaScript Unit Tests | |
| run: ./gradlew :server:jsTest | |
| - name: Run Server Migration Tests | |
| run: ./gradlew :server:verifySqlDelightMigration | |
| - name: Run Round-Trip Sync Integration Tests | |
| id: integration-tests | |
| run: ./gradlew :integrationTests:jvmTest | |
| - name: Upload integration test report on failure | |
| if: failure() && steps.integration-tests.outcome == 'failure' | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: integration-tests-report | |
| path: | | |
| integrationTests/build/reports/tests/jvmTest/ | |
| integrationTests/build/test-results/jvmTest/ | |
| static-analysis: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Install Semgrep | |
| run: pip install semgrep==1.170.0 | |
| # Self-contained code-pattern rules under .semgrep/ (no registry fetch, no account). | |
| # Honors `// nosemgrep` comments. `--error` makes an unsuppressed finding fail the build. | |
| - name: Run Semgrep (custom rules) | |
| run: semgrep scan --config .semgrep --error --metrics off | |
| # Pinned for reproducibility — a tool auto-update must never spontaneously fail an | |
| # unrelated PR's gate. Bump deliberately to pick up new secret-detection rules. | |
| - name: Install gitleaks | |
| env: | |
| GITLEAKS_VERSION: 8.30.1 | |
| run: | | |
| curl -sSL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \ | |
| | sudo tar -xz -C /usr/local/bin gitleaks | |
| gitleaks version | |
| # Fails the build on any committed secret. Known test fixtures are allowlisted in | |
| # .gitleaks.toml; the JSON report is parsed so failures list rule + file:line. | |
| - name: Run gitleaks | |
| run: | | |
| gitleaks dir . --config .gitleaks.toml --no-banner --redact \ | |
| --report-format json --report-path gitleaks-report.json --exit-code 2 || true | |
| count=$(python3 -c "import json; print(len(json.load(open('gitleaks-report.json'))))") | |
| if [ "$count" -gt 0 ]; then | |
| echo "::error::gitleaks found $count secret(s):" | |
| python3 -c "import json; [print(f\" {x['RuleID']} {x['File']}:{x['StartLine']}\") for x in json.load(open('gitleaks-report.json'))]" | |
| exit 1 | |
| fi | |
| echo "gitleaks: no leaks" | |
| android-instrumented-tests: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: set up JDK 21 | |
| uses: actions/setup-java@v5 | |
| with: | |
| java-version: '21' | |
| distribution: 'temurin' | |
| cache: gradle | |
| - name: Grant execute permission for gradlew | |
| run: chmod +x gradlew | |
| # The hardware-accelerated emulator needs KVM, which ubuntu-latest exposes once permissioned. | |
| - name: Enable KVM | |
| run: | | |
| echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules | |
| sudo udevadm control --reload-rules | |
| sudo udevadm trigger --name-match=kvm | |
| # Cache the emulator system image + AVD snapshot so subsequent runs skip the slow cold | |
| # boot. Keyed on every parameter that defines the AVD - bump the key if any `with:` value | |
| # below changes, or the cached snapshot won't match. | |
| - name: AVD cache | |
| uses: actions/cache@v6 | |
| id: avd-cache | |
| with: | |
| path: | | |
| ~/.android/avd/* | |
| ~/.android/adb* | |
| key: avd-api34-x86_64-google_apis-pixel_6 | |
| # On a cache miss, cold-boot once and let the action save a snapshot for the cache. No tests here. | |
| - name: Create AVD and generate snapshot for caching | |
| if: steps.avd-cache.outputs.cache-hit != 'true' | |
| uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2.38.0 | |
| with: | |
| api-level: 34 | |
| arch: x86_64 | |
| target: google_apis | |
| profile: pixel_6 | |
| force-avd-creation: false | |
| emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none | |
| script: echo "Generated AVD snapshot for caching." | |
| - name: Run instrumented tests on emulator | |
| id: instrumented-tests | |
| uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2.38.0 | |
| with: | |
| api-level: 34 | |
| arch: x86_64 | |
| target: google_apis | |
| profile: pixel_6 | |
| force-avd-creation: false | |
| emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none | |
| script: ./gradlew :android:connectedDebugAndroidTest | |
| - name: Upload instrumented test report on failure | |
| if: failure() && steps.instrumented-tests.outcome == 'failure' | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: instrumented-tests-report | |
| path: | | |
| android/build/reports/androidTests/connected/ | |
| android/build/outputs/androidTest-results/connected/ | |
| # The ubuntu `build` job never touches the iOS targets, so Native-only breakage | |
| # in commonMain slips through otherwise. This job both compiles the iOS targets | |
| # and runs the Kotlin/Native unit tests on the simulator. | |
| ios-compile: | |
| name: iOS compile & test | |
| runs-on: macos-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Select Xcode 26 | |
| # macos-latest still defaults to Xcode 16.4 (iOS 18.5 SDK), but linking the | |
| # iOS test executable pulls in Compose's ui-uikit, which references symbols | |
| # (e.g. UIViewLayoutRegion) that only exist in the iOS 26 SDK. Without this | |
| # the link fails non-deterministically depending on the runner image. Glob | |
| # keeps us off a pinned patch version that could vanish on a future image. | |
| run: | | |
| XCODE_APP="$(ls -d /Applications/Xcode_26*.app | sort -V | tail -1)" | |
| echo "Using $XCODE_APP" | |
| sudo xcode-select -s "$XCODE_APP/Contents/Developer" | |
| xcodebuild -version | |
| - name: set up JDK 21 | |
| uses: actions/setup-java@v5 | |
| with: | |
| java-version: '21' | |
| distribution: 'temurin' | |
| cache: gradle | |
| - name: Grant execute permission for gradlew | |
| run: chmod +x gradlew | |
| - name: Compile iOS Kotlin targets | |
| run: ./gradlew :composeUi:compileKotlinIosArm64 :composeUi:compileKotlinIosSimulatorArm64 | |
| - name: Run iOS unit tests | |
| id: ios-tests | |
| run: ./gradlew :common:iosSimulatorArm64Test | |
| - name: Upload iOS test report on failure | |
| if: failure() && steps.ios-tests.outcome == 'failure' | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: ios-tests-report | |
| path: | | |
| common/build/reports/tests/iosSimulatorArm64Test/ | |
| common/build/test-results/iosSimulatorArm64Test/ | |
| # Runs the real iOS app on a simulator and drives it through XCUITest (the iOS analogue of the | |
| # android-instrumented-tests job). The whole UI is Compose Multiplatform, so the tests target | |
| # Compose testTags, which the app exposes to the iOS accessibility tree when launched with | |
| # --uitesting (see composeUi .../MainViewController.kt and ios/iosUITests). | |
| ios-ui-tests: | |
| name: iOS UI tests | |
| runs-on: macos-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Select Xcode 26 | |
| # Match ios-compile: linking Compose's ui-uikit needs the iOS 26 SDK. | |
| run: | | |
| XCODE_APP="$(ls -d /Applications/Xcode_26*.app | sort -V | tail -1)" | |
| echo "Using $XCODE_APP" | |
| sudo xcode-select -s "$XCODE_APP/Contents/Developer" | |
| xcodebuild -version | |
| - name: set up JDK 21 | |
| uses: actions/setup-java@v5 | |
| with: | |
| java-version: '21' | |
| distribution: 'temurin' | |
| cache: gradle | |
| - name: Grant execute permission for gradlew | |
| run: chmod +x gradlew | |
| # Pick the newest available iPhone simulator rather than pinning a device that a future | |
| # Xcode image might drop, then boot it so the test run doesn't race first-boot. | |
| - name: Select & boot a simulator | |
| id: sim | |
| run: | | |
| UDID=$(xcrun simctl list devices available --json \ | |
| | jq -r '[.devices[][] | select(.name | startswith("iPhone"))] | last | .udid') | |
| if [ -z "$UDID" ] || [ "$UDID" = "null" ]; then | |
| echo "No iPhone simulator available"; xcrun simctl list devices available; exit 1 | |
| fi | |
| echo "Using simulator $UDID" | |
| # Disable the hardware keyboard before boot so the software keyboard shows — Compose | |
| # text fields need it for XCUITest text entry (see ios/scripts). | |
| bash ios/scripts/disable_sim_hardware_keyboard.sh | |
| xcrun simctl boot "$UDID" || true | |
| echo "udid=$UDID" >> "$GITHUB_OUTPUT" | |
| - name: Run iOS UI tests | |
| id: ios-ui-tests | |
| run: | | |
| set -o pipefail | |
| xcodebuild test \ | |
| -project ios/ios.xcodeproj \ | |
| -scheme iosUITests \ | |
| -destination "id=${{ steps.sim.outputs.udid }}" \ | |
| -configuration Debug \ | |
| -resultBundlePath ios-ui-tests.xcresult \ | |
| CODE_SIGNING_ALLOWED=NO | |
| - name: Upload iOS UI test results on failure | |
| if: failure() && steps.ios-ui-tests.outcome == 'failure' | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: ios-ui-tests-report | |
| path: ios-ui-tests.xcresult | |
| # ios: | |
| # runs-on: macos-latest | |
| # steps: | |
| # - name: Checkout | |
| # uses: actions/checkout@v4 | |
| # | |
| # - name: check Xcode version | |
| # run: /usr/bin/xcodebuild -version | |
| # | |
| # - name: Get swift version | |
| # run: swift --version | |
| # | |
| # - name: set up JDK 21 | |
| # uses: actions/setup-java@v4 | |
| # with: | |
| # java-version: '21' | |
| # distribution: 'temurin' | |
| # cache: gradle | |
| # | |
| # - name: Grant execute permission for gradlew | |
| # run: chmod +x gradlew | |
| # | |
| # - name: Install the Apple certificate and provisioning profile | |
| # env: | |
| # BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} | |
| # P12_PASSWORD: ${{ secrets.P12_PASSWORD }} | |
| # BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }} | |
| # KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} | |
| # run: | | |
| # # create variables | |
| # CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 | |
| # PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision | |
| # KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db | |
| # | |
| # # import certificate and provisioning profile from secrets | |
| # echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o "$CERTIFICATE_PATH" | |
| # echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o "$PP_PATH" | |
| # | |
| # # create temporary 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 to keychain | |
| # security import "$CERTIFICATE_PATH" -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH" | |
| # security list-keychain -d user -s "$KEYCHAIN_PATH" | |
| # | |
| # # apply provisioning profile | |
| # mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles | |
| # cp "$PP_PATH" ~/Library/MobileDevice/Provisioning\ Profiles | |
| # | |
| # - name: Set Default Scheme | |
| # run: | | |
| # cd ios | |
| # default="ios" | |
| # echo $default | cat >default | |
| # echo Using default scheme: $default | |
| # | |
| # - name: Build | |
| # env: | |
| # scheme: ${{ 'default' }} | |
| # platform: ${{ 'iOS Simulator' }} | |
| # run: | | |
| # cd ios | |
| # # xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959) | |
| # device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"` | |
| # if [ $scheme = default ]; then scheme=$(cat default); fi | |
| # if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi | |
| # file_to_build=`echo $file_to_build | awk '{$1=$1;print}'` | |
| # xcodebuild build-for-testing -scheme "$scheme" -sdk iphoneos -allowProvisioningUpdates |