diff --git a/.github/workflows/crowdin-download.yml b/.github/workflows/crowdin-download.yml deleted file mode 100644 index 07dea7363..000000000 --- a/.github/workflows/crowdin-download.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Crowdin Download Translations Action - -on: - schedule: # Every Sunday at midnight - - cron: '0 */1 * * *' - workflow_dispatch: # Allow manual triggering - -jobs: - synchronize-with-crowdin: - runs-on: ubuntu-latest - if: github.repository == 'meshtastic/Meshtastic-Android' - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Download translations with Crowdin - uses: crowdin/github-action@v2 - with: - base_url: 'https://meshtastic.crowdin.com/api/v2' - config: 'config/crowdin/crowdin.yml' - upload_sources: false - upload_translations: false - download_translations: true - localization_branch_name: l10n_crowdin_translations - commit_message: 'chore(l10n): New Crowdin Translations by GitHub Action' - create_pull_request: true - pull_request_title: 'chore(l10n): New Crowdin Translations' - pull_request_body: 'New Crowdin translations by [Crowdin GH Action](https://github.com/crowdin/github-action)' - pull_request_base_branch_name: 'main' - pull_request_labels: 'l10n' - crowdin_branch_name: 'main' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} - CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} diff --git a/.github/workflows/crowdin-upload-sources.yml b/.github/workflows/crowdin-upload-sources.yml deleted file mode 100644 index 9cde7f7a0..000000000 --- a/.github/workflows/crowdin-upload-sources.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Crowdin Upload Sources Action - -on: - push: # Watch source strings.xml for changes on main - paths: [ 'app/src/main/res/values/strings.xml' ] - branches: [ main ] - workflow_dispatch: # Allow manual triggering - -jobs: - synchronize-with-crowdin: - runs-on: ubuntu-latest - if: github.repository == 'meshtastic/Meshtastic-Android' - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Upload sources with Crowdin - uses: crowdin/github-action@v2 - with: - base_url: 'https://meshtastic.crowdin.com/api/v2' - config: 'config/crowdin/crowdin.yml' - upload_sources: true - upload_translations: false - download_translations: false - crowdin_branch_name: 'main' - env: - CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} - CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} diff --git a/.github/workflows/merge-queue.yml b/.github/workflows/merge-queue.yml index 4311fe9df..06d268e71 100644 --- a/.github/workflows/merge-queue.yml +++ b/.github/workflows/merge-queue.yml @@ -9,143 +9,20 @@ concurrency: cancel-in-progress: true jobs: - build: - runs-on: ubuntu-latest + build_and_detekt: if: github.repository == 'meshtastic/Meshtastic-Android' - timeout-minutes: 30 - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: 'recursive' - - name: Validate Gradle wrapper - uses: gradle/actions/wrapper-validation@v4 - - name: Set up JDK 21 - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'jetbrains' - - name: Cache Gradle User Home - uses: actions/cache@v4 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'gradle.properties', 'settings.gradle*') }} - restore-keys: | - gradle-${{ runner.os }}- - - name: Cache Android build cache - uses: actions/cache@v4 - with: - path: ~/.android/build-cache - key: android-build-cache-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'gradle.properties', 'settings.gradle*') }} - restore-keys: | - android-build-cache-${{ runner.os }}- - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - with: - cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - build-scan-publish: true - build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service' - build-scan-terms-of-use-agree: 'yes' - add-job-summary: always - - name: Check lint - run: ./gradlew lintFdroidDebug lintGoogleDebug --configuration-cache --scan - - name: Build debug artifacts - run: ./gradlew assembleDebug --configuration-cache --scan - - name: Run local tests - run: ./gradlew testFdroidDebug testGoogleDebug --configuration-cache --scan - - detekt: - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: 'recursive' - - name: Set up JDK 21 - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'jetbrains' - - name: Cache Gradle User Home - uses: actions/cache@v4 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'gradle.properties', 'settings.gradle*') }} - restore-keys: | - gradle-${{ runner.os }}- - - name: Cache Android build cache - uses: actions/cache@v4 - with: - path: ~/.android/build-cache - key: android-build-cache-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'gradle.properties', 'settings.gradle*') }} - restore-keys: | - android-build-cache-${{ runner.os }}- - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - with: - cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - build-scan-publish: true - build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service' - build-scan-terms-of-use-agree: 'yes' - add-job-summary: always - - name: Check detekt - run: ./gradlew detekt --configuration-cache --scan + uses: ./.github/workflows/reusable-android-build.yml + with: + upload_artifacts: false + secrets: + GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }} androidTest: - runs-on: ubuntu-latest - timeout-minutes: 15 - strategy: - matrix: - api-level: [26, 35] - steps: - - uses: actions/checkout@v4 - with: - submodules: 'recursive' - - name: Enable KVM group perms - 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 - - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'jetbrains' - - uses: gradle/actions/setup-gradle@v4 - with: - cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - build-scan-publish: true - build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service' - build-scan-terms-of-use-agree: 'yes' - add-job-summary: always - - uses: actions/cache@v4 - id: avd-cache - with: - path: | - ~/.android/avd/* - ~/.android/adb* - key: avd-${{ matrix.api-level }} - - name: create AVD and generate snapshot for caching - if: steps.avd-cache.outputs.cache-hit != 'true' - uses: reactivecircus/android-emulator-runner@v2 - with: - api-level: ${{ matrix.api-level }} - arch: x86_64 - force-avd-creation: false - emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - disable-animations: true - script: echo "Generated AVD snapshot for caching." - - uses: reactivecircus/android-emulator-runner@v2 - env: - ANDROID_EMULATOR_WAIT_TIME_BEFORE_KILL: 60 - with: - api-level: ${{ matrix.api-level }} - arch: x86_64 - force-avd-creation: false - emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -no-metrics -camera-back none - disable-animations: true - script: ./gradlew :app:connectedFdroidDebugAndroidTest --configuration-cache --scan && ( killall -INT crashpad_handler || true ) + if: github.repository == 'meshtastic/Meshtastic-Android' + uses: ./.github/workflows/reusable-android-test.yml + with: + api_levels: '[26, 35]' # Run on both API 26 and 35 for merge queue + upload_artifacts: false + secrets: + GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }} + diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index bdf41ef29..0621944b3 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -9,167 +9,19 @@ concurrency: cancel-in-progress: true jobs: - build: - runs-on: ubuntu-latest + build_and_detekt: if: github.repository == 'meshtastic/Meshtastic-Android' - timeout-minutes: 30 - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: 'recursive' - - name: Validate Gradle wrapper - uses: gradle/actions/wrapper-validation@v4 - - name: Set up JDK 21 - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'jetbrains' - - name: Cache Gradle User Home - uses: actions/cache@v4 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'gradle.properties', 'settings.gradle*') }} - restore-keys: | - gradle-${{ runner.os }}- - - name: Cache Android build cache - uses: actions/cache@v4 - with: - path: ~/.android/build-cache - key: android-build-cache-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'gradle.properties', 'settings.gradle*') }} - restore-keys: | - android-build-cache-${{ runner.os }}- - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - with: - cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - build-scan-publish: true - build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service' - build-scan-terms-of-use-agree: 'yes' - add-job-summary: always - - name: Check lint - run: ./gradlew lintFdroidDebug lintGoogleDebug --configuration-cache --scan - - name: Build debug artifacts - run: ./gradlew assembleDebug --configuration-cache --scan - - name: Run local tests - run: ./gradlew testFdroidDebug testGoogleDebug --configuration-cache --scan - - name: Upload debug artifact - uses: actions/upload-artifact@v4 - with: - name: fdroidDebug - path: app/build/outputs/apk/fdroid/debug/app-fdroid-debug.apk - retention-days: 14 - - name: Upload build reports - uses: actions/upload-artifact@v4 - with: - name: build-reports - path: app/build/reports - retention-days: 14 - - detekt: - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: 'recursive' - - name: Set up JDK 21 - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'jetbrains' - - name: Cache Gradle User Home - uses: actions/cache@v4 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'gradle.properties', 'settings.gradle*') }} - restore-keys: | - gradle-${{ runner.os }}- - - name: Cache Android build cache - uses: actions/cache@v4 - with: - path: ~/.android/build-cache - key: android-build-cache-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'gradle.properties', 'settings.gradle*') }} - restore-keys: | - android-build-cache-${{ runner.os }}- - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - with: - cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - build-scan-publish: true - build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service' - build-scan-terms-of-use-agree: 'yes' - add-job-summary: always - - name: Check detekt - run: ./gradlew detekt --configuration-cache --scan - - name: Upload build reports - uses: actions/upload-artifact@v4 - with: - name: detekt-reports - path: app/build/reports - retention-days: 14 + uses: ./.github/workflows/reusable-android-build.yml + secrets: + GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }} + # inputs.upload_artifacts defaults to true, so no need to specify for PRs androidTest: - runs-on: ubuntu-latest - timeout-minutes: 15 - strategy: - matrix: - api-level: [26, 35] - steps: - - uses: actions/checkout@v4 - with: - submodules: 'recursive' - - name: Enable KVM group perms - 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 - - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'jetbrains' - - uses: gradle/actions/setup-gradle@v4 - with: - cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - build-scan-publish: true - build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service' - build-scan-terms-of-use-agree: 'yes' - add-job-summary: always - - uses: actions/cache@v4 - id: avd-cache - with: - path: | - ~/.android/avd/* - ~/.android/adb* - key: avd-${{ matrix.api-level }} - - name: create AVD and generate snapshot for caching - if: steps.avd-cache.outputs.cache-hit != 'true' - uses: reactivecircus/android-emulator-runner@v2 - with: - api-level: ${{ matrix.api-level }} - arch: x86_64 - force-avd-creation: false - emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - disable-animations: true - script: echo "Generated AVD snapshot for caching." - - uses: reactivecircus/android-emulator-runner@v2 - env: - ANDROID_EMULATOR_WAIT_TIME_BEFORE_KILL: 60 - with: - api-level: ${{ matrix.api-level }} - arch: x86_64 - force-avd-creation: false - emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -no-metrics -camera-back none - disable-animations: true - script: ./gradlew :app:connectedFdroidDebugAndroidTest --configuration-cache --scan && ( killall -INT crashpad_handler || true ) - - name: Upload Test Results - uses: actions/upload-artifact@v4 - with: - name: android-test-reports-api-${{ matrix.api-level }} - path: app/build/outputs/androidTest-results/ - retention-days: 14 + # AssumingandroidTest should also only run for the main repository + if: github.repository == 'meshtastic/Meshtastic-Android' + uses: ./.github/workflows/reusable-android-test.yml + with: + api_levels: '[35]' # Run only on API 35 for PRs + # upload_artifacts defaults to true, so no need to explicitly set + secrets: + GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }} diff --git a/.github/workflows/reusable-android-build.yml b/.github/workflows/reusable-android-build.yml new file mode 100644 index 000000000..7795fe17c --- /dev/null +++ b/.github/workflows/reusable-android-build.yml @@ -0,0 +1,79 @@ +name: Reusable Android Build and Detekt + +on: + workflow_call: + inputs: + upload_artifacts: + description: 'Whether to upload build and Detekt artifacts' + required: false + type: boolean + default: true + secrets: + GRADLE_ENCRYPTION_KEY: + required: false + +jobs: + build_and_detekt: + runs-on: ubuntu-latest + timeout-minutes: 35 + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: 'recursive' + - name: Validate Gradle wrapper + uses: gradle/actions/wrapper-validation@v4 + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'jetbrains' + - name: Cache Gradle User Home + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'gradle.properties', 'settings.gradle*') }} + restore-keys: | + gradle-${{ runner.os }}- + - name: Cache Android build cache + uses: actions/cache@v4 + with: + path: ~/.android/build-cache + key: android-build-cache-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'gradle.properties', 'settings.gradle*') }} + restore-keys: | + android-build-cache-${{ runner.os }}- + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + with: + cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} + build-scan-publish: true + build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service' + build-scan-terms-of-use-agree: 'yes' + add-job-summary: always + - name: Run Detekt, Build, Lint, and Local Tests + run: ./gradlew detekt lintFdroidDebug lintGoogleDebug assembleDebug testFdroidDebug testGoogleDebug --configuration-cache --scan --parallel + - name: Upload F-Droid debug artifact + if: ${{ inputs.upload_artifacts }} + uses: actions/upload-artifact@v4 + with: + name: fdroidDebug + path: app/build/outputs/apk/fdroid/debug/app-fdroid-debug.apk + retention-days: 14 + - name: Upload Google debug artifact + if: ${{ inputs.upload_artifacts }} + uses: actions/upload-artifact@v4 + with: + name: googleDebug + path: app/build/outputs/apk/google/debug/app-google-debug.apk + retention-days: 14 + - name: Upload build and Detekt reports + if: ${{ inputs.upload_artifacts }} + uses: actions/upload-artifact@v4 + with: + name: build-and-detekt-reports + path: | + app/build/reports + **/build/reports/detekt + retention-days: 14 diff --git a/.github/workflows/reusable-android-test.yml b/.github/workflows/reusable-android-test.yml new file mode 100644 index 000000000..3c31c74a0 --- /dev/null +++ b/.github/workflows/reusable-android-test.yml @@ -0,0 +1,96 @@ +name: Reusable Android Instrumented Tests + +on: + workflow_call: + inputs: + upload_artifacts: + description: 'Whether to upload Android test reports' + required: false + type: boolean + default: true + api_levels: + description: 'JSON array string of API levels to run tests on (e.g., `[35]` or `[26, 35]`)' + required: false + type: string + default: '[26, 35]' # Default to running both if not specified by caller + secrets: + GRADLE_ENCRYPTION_KEY: + required: false + +jobs: + androidTest: + runs-on: ubuntu-latest + timeout-minutes: 15 + strategy: + matrix: + api-level: ${{ fromJson(inputs.api_levels) }} # Use the input to define the matrix + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: 'recursive' + - name: Enable KVM group perms + 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 + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'jetbrains' + - name: Cache Gradle User Home + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('''**/*.gradle*''', '''**/gradle-wrapper.properties*''', '''gradle.properties*''', '''settings.gradle*''') }} + restore-keys: | + gradle-${{ runner.os }}- + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + with: + cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} + build-scan-publish: true + build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service' + build-scan-terms-of-use-agree: 'yes' + add-job-summary: always + - name: Cache AVD + uses: actions/cache@v4 + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/adb* + key: avd-${{ matrix.api-level }}-${{ hashFiles('''**/*.gradle*''', '''**/gradle-wrapper.properties*''') }} + restore-keys: | + avd-${{ matrix.api-level }}- + - name: Create AVD and generate snapshot for caching + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ matrix.api-level }} + arch: x86_64 + force-avd-creation: false + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + disable-animations: true + script: echo "Generated AVD snapshot for caching." + - name: Run Android Instrumented Tests + uses: reactivecircus/android-emulator-runner@v2 + env: + ANDROID_EMULATOR_WAIT_TIME_BEFORE_KILL: 60 + with: + api-level: ${{ matrix.api-level }} + arch: x86_64 + force-avd-creation: false + emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -no-metrics -camera-back none + disable-animations: true + script: ./gradlew :app:connectedFdroidDebugAndroidTest :app:connectedGoogleDebugAndroidTest --configuration-cache --scan && ( killall -INT crashpad_handler || true ) + - name: Upload Test Results + if: ${{ inputs.upload_artifacts }} + uses: actions/upload-artifact@v4 + with: + name: android-test-reports-api-${{ matrix.api-level }} + path: app/build/outputs/androidTest-results/ + retention-days: 14 diff --git a/.github/workflows/scheduled-updates.yml b/.github/workflows/scheduled-updates.yml new file mode 100644 index 000000000..70f87c3cb --- /dev/null +++ b/.github/workflows/scheduled-updates.yml @@ -0,0 +1,117 @@ +name: Scheduled Updates (Firmware, Hardware, Translations) + +on: + schedule: + - cron: '0 * * * *' # Run every hour + workflow_dispatch: # Allow manual triggering + +jobs: + update_and_translate: + runs-on: ubuntu-latest + if: github.repository == 'meshtastic/Meshtastic-Android' + permissions: + contents: write # To commit files and push branches + pull-requests: write # To create pull requests + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Update firmware releases list + run: | + firmware_file_path="app/src/main/assets/firmware_releases.json" + temp_firmware_file="/tmp/new_firmware_releases.json" + + echo "Fetching latest firmware releases..." + curl -s --fail https://api.meshtastic.org/github/firmware/list > "$temp_firmware_file" + + if ! jq empty "$temp_firmware_file" 2>/dev/null; then + echo "::error::Firmware API returned invalid JSON data. Skipping firmware update." + else + if [ ! -f "$firmware_file_path" ] || ! jq --sort-keys . "$temp_firmware_file" | diff -q - <(jq --sort-keys . "$firmware_file_path"); then + echo "Changes detected in firmware list or local file missing. Updating $firmware_file_path." + cp "$temp_firmware_file" "$firmware_file_path" + else + echo "No changes detected in firmware list." + fi + fi + + - name: Update hardware list + run: | + hardware_file_path="app/src/main/assets/device_hardware.json" + temp_hardware_file="/tmp/new_device_hardware.json" + + echo "Fetching latest device hardware data..." + curl -s --fail https://api.meshtastic.org/resource/deviceHardware > "$temp_hardware_file" + + if ! jq empty "$temp_hardware_file" 2>/dev/null; then + echo "::error::Hardware API returned invalid JSON data. Skipping hardware update." + else + if [ ! -f "$hardware_file_path" ] || ! jq --sort-keys . "$temp_hardware_file" | diff -q - <(jq --sort-keys . "$hardware_file_path"); then + echo "Changes detected in hardware list or local file missing. Updating $hardware_file_path." + cp "$temp_hardware_file" "$hardware_file_path" + else + echo "No changes detected in hardware list." + fi + fi + + - name: Upload sources to Crowdin + uses: crowdin/github-action@v2 + with: + base_url: 'https://meshtastic.crowdin.com/api/v2' + config: 'config/crowdin/crowdin.yml' + crowdin_branch_name: 'main' + upload_sources: true + upload_translations: false + download_translations: false + create_pull_request: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} + CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} + + - name: Download translations from Crowdin + uses: crowdin/github-action@v2 + with: + base_url: 'https://meshtastic.crowdin.com/api/v2' + config: 'config/crowdin/crowdin.yml' + crowdin_branch_name: 'main' + upload_sources: false + download_translations: true + create_pull_request: false + commit_message: 'chore(l10n): New Crowdin Translations from scheduled update' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} + CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} + + - name: Create Pull Request if changes occurred + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: | + chore: Scheduled updates (Firmware, Hardware, Translations) + + Automated updates for: + - Firmware releases list + - Device hardware list + - Crowdin source string uploads + - Crowdin translation downloads + title: 'chore: Scheduled updates (Firmware, Hardware, Translations)' + body: | + This PR includes automated updates from the scheduled workflow: + + - Updated `firmware_releases.json` from the Meshtastic API (if changed). + - Updated `device_hardware.json` from the Meshtastic API (if changed). + - Source strings were uploaded to Crowdin (if any local changes were pushed). + - Latest translations were downloaded from Crowdin (if available). + + Please review the changes. + branch: 'scheduled-updates' + base: 'main' + delete-branch: true + labels: | + automation + l10n + firmware + hardware \ No newline at end of file diff --git a/.github/workflows/update-firmware-releases-list.yml b/.github/workflows/update-firmware-releases-list.yml deleted file mode 100644 index 3a6b7d2be..000000000 --- a/.github/workflows/update-firmware-releases-list.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Update Firmware Releases List - -on: - schedule: - - cron: '0 * * * *' # Run every hour - workflow_dispatch: # Allow manual triggering - -jobs: - update-hardware-list: - runs-on: ubuntu-latest - if: github.repository == 'meshtastic/Meshtastic-Android' - permissions: - contents: write - pull-requests: write - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '22' - - - name: Fetch latest firmware releases data - id: fetch-data - run: | - # Define variables for file paths - firmware_releases_json="app/src/main/assets/firmware_releases.json" - new_firmware_releases_json="/tmp/new_firmware_releases.json" - - # Fetch data from API - curl -s --fail https://api.meshtastic.org/github/firmware/list > "$new_firmware_releases_json" - - # Ensure the output is valid JSON - if ! jq empty "$new_firmware_releases_json" 2>/dev/null; then - echo "::error::API returned invalid JSON data" - exit 1 - fi - - # Check if "$firmware_releases_json" exists - if [ -f "$firmware_releases_json" ]; then - # Format both files for consistent comparison - jq --sort-keys . "$new_firmware_releases_json" > /tmp/new-formatted.json - jq --sort-keys . "$firmware_releases_json" > /tmp/existing-formatted.json - - # Compare files - if cmp -s /tmp/new-formatted.json /tmp/existing-formatted.json; then - echo "No changes detected in hardware list" - echo "has_changes=false" >> $GITHUB_OUTPUT - else - echo "Changes detected in hardware list" - echo "has_changes=true" >> $GITHUB_OUTPUT - fi - else - echo "firmware_releases.json doesn't exist yet" - echo "has_changes=true" >> $GITHUB_OUTPUT - fi - - # Copy new data to destination - cp "$new_firmware_releases_json" "$firmware_releases_json" - - - name: Create Pull Request - if: steps.fetch-data.outputs.has_changes == 'true' - uses: peter-evans/create-pull-request@v7 - with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: "chore: update firmware releases list from Meshtastic API" - title: "chore: update firmware releases list from Meshtastic API" - body: | - This PR updates the firmware releases list with the latest data from the Meshtastic API. - - This PR was automatically generated by the update-hardware-list workflow. - branch: update-hardware-list - base: main - delete-branch: true diff --git a/.github/workflows/update-hardware-list.yml b/.github/workflows/update-hardware-list.yml deleted file mode 100644 index dbfb8c1db..000000000 --- a/.github/workflows/update-hardware-list.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Update Hardware List - -on: - schedule: - - cron: '0 * * * *' # Run every hour - workflow_dispatch: # Allow manual triggering - -jobs: - update-hardware-list: - runs-on: ubuntu-latest - if: github.repository == 'meshtastic/Meshtastic-Android' - permissions: - contents: write - pull-requests: write - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '22' - - - name: Fetch latest device hardware data - id: fetch-data - run: | - # Define variables for file paths - device_hardware_json="app/src/main/assets/device_hardware.json" - new_device_hardware_json="/tmp/new_device_hardware.json" - - # Fetch data from API - curl -s --fail https://api.meshtastic.org/resource/deviceHardware > "$new_device_hardware_json" - - # Ensure the output is valid JSON - if ! jq empty "$new_device_hardware_json" 2>/dev/null; then - echo "::error::API returned invalid JSON data" - exit 1 - fi - - # Check if "$device_hardware_json" exists - if [ -f "$device_hardware_json" ]; then - # Format both files for consistent comparison - jq --sort-keys . "$new_device_hardware_json" > /tmp/new-formatted.json - jq --sort-keys . "$device_hardware_json" > /tmp/existing-formatted.json - - # Compare files - if cmp -s /tmp/new-formatted.json /tmp/existing-formatted.json; then - echo "No changes detected in hardware list" - echo "has_changes=false" >> $GITHUB_OUTPUT - else - echo "Changes detected in hardware list" - echo "has_changes=true" >> $GITHUB_OUTPUT - fi - else - echo "device_hardware.json doesn't exist yet" - echo "has_changes=true" >> $GITHUB_OUTPUT - fi - - # Copy new data to destination - cp "$new_device_hardware_json" "$device_hardware_json" - - - name: Create Pull Request - if: steps.fetch-data.outputs.has_changes == 'true' - uses: peter-evans/create-pull-request@v7 - with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: "chore: update device hardware list from Meshtastic API" - title: "chore: update device hardware list from Meshtastic API" - body: | - This PR updates the device hardware list with the latest data from the Meshtastic API. - - This PR was automatically generated by the update-hardware-list workflow. - branch: update-hardware-list - base: main - delete-branch: true diff --git a/settings.gradle.kts b/settings.gradle.kts index ac202999b..481c9c10a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,8 +20,19 @@ rootProject.name = "Meshtastic Android" plugins { id("org.gradle.toolchains.foojay-resolver") version "1.0.0" + id("com.gradle.develocity") version("4.0.2") } +develocity { + buildScan { + capture { + fileFingerprints.set(true) + } + publishing.onlyIf { false } + } +} + +@Suppress("UnstableApiUsage") toolchainManagement { jvm { javaRepositories {