name: Scheduled Updates (Firmware, Hardware, Translations) on: schedule: - cron: '0 */4 * * *' # Run every 4 hours (was hourly — reduced to cut cascade CI cost) workflow_dispatch: # Allow manual triggering jobs: update_assets: runs-on: ubuntu-24.04 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@v6 with: token: ${{ secrets.CROWDIN_GITHUB_TOKEN }} - name: Update firmware releases list id: firmware run: | firmware_file_path="androidApp/src/main/assets/firmware_releases.json" temp_firmware_file="/tmp/new_firmware_releases.json" echo "Fetching latest firmware releases..." http_code=$(curl -s -o "$temp_firmware_file" -w '%{http_code}' https://api.meshtastic.org/github/firmware/list || true) http_code="${http_code:-0}" if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then echo "::warning::Firmware API returned HTTP $http_code. Skipping firmware update." echo "status=error" >> "$GITHUB_OUTPUT" echo "detail=HTTP $http_code from firmware API" >> "$GITHUB_OUTPUT" elif ! jq empty "$temp_firmware_file" 2>/dev/null; then echo "::warning::Firmware API returned invalid JSON data. Skipping firmware update." echo "status=error" >> "$GITHUB_OUTPUT" echo "detail=Invalid JSON response from firmware API" >> "$GITHUB_OUTPUT" 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" echo "status=updated" >> "$GITHUB_OUTPUT" else echo "No changes detected in firmware list." echo "status=unchanged" >> "$GITHUB_OUTPUT" fi fi - name: Update hardware list id: hardware run: | hardware_file_path="androidApp/src/main/assets/device_hardware.json" temp_hardware_file="/tmp/new_device_hardware.json" echo "Fetching latest device hardware data..." http_code=$(curl -s -o "$temp_hardware_file" -w '%{http_code}' https://api.meshtastic.org/resource/deviceHardware || true) http_code="${http_code:-0}" if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then echo "::warning::Hardware API returned HTTP $http_code. Skipping hardware update." echo "status=error" >> "$GITHUB_OUTPUT" echo "detail=HTTP $http_code from hardware API" >> "$GITHUB_OUTPUT" elif ! jq empty "$temp_hardware_file" 2>/dev/null; then echo "::warning::Hardware API returned invalid JSON data. Skipping hardware update." echo "status=error" >> "$GITHUB_OUTPUT" echo "detail=Invalid JSON response from hardware API" >> "$GITHUB_OUTPUT" 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" echo "status=updated" >> "$GITHUB_OUTPUT" else echo "No changes detected in hardware list." echo "status=unchanged" >> "$GITHUB_OUTPUT" fi fi - name: Sync with Crowdin uses: crowdin/github-action@v2 with: base_url: 'https://meshtastic.crowdin.com/api/v2' config: 'crowdin.yml' crowdin_branch_name: 'main' upload_sources: true upload_sources_args: '--preserve-hierarchy' upload_translations: false download_translations: true download_translations_args: '--preserve-hierarchy' create_pull_request: false commit_message: 'chore(l10n): New Crowdin Translations from scheduled update' push_translations: false push_sources: false localization_branch_name: ${{ github.ref_name }} env: GITHUB_TOKEN: ${{ secrets.CROWDIN_GITHUB_TOKEN }} CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} - name: Fix file permissions run: sudo chown -R $USER:$USER . - name: Gradle Setup uses: ./.github/actions/gradle-setup with: gradle_encryption_key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} cache_read_only: 'false' - name: Update Graphs run: ./gradlew graphUpdate continue-on-error: true - name: Build PR body id: pr_body run: | firmware_status="${{ steps.firmware.outputs.status }}" firmware_detail="${{ steps.firmware.outputs.detail }}" hardware_status="${{ steps.hardware.outputs.status }}" hardware_detail="${{ steps.hardware.outputs.detail }}" body="This PR includes automated updates from the scheduled workflow:" body+=$'\n' # Firmware status case "$firmware_status" in updated) body+=$'\n'"- ✅ \`firmware_releases.json\` updated from the Meshtastic API." ;; unchanged) body+=$'\n'"- ✔️ \`firmware_releases.json\` checked — no changes detected." ;; error) body+=$'\n'"- ⚠️ \`firmware_releases.json\` skipped — ${firmware_detail}." ;; *) body+=$'\n'"- ❓ \`firmware_releases.json\` — unknown status." ;; esac # Hardware status case "$hardware_status" in updated) body+=$'\n'"- ✅ \`device_hardware.json\` updated from the Meshtastic API." ;; unchanged) body+=$'\n'"- ✔️ \`device_hardware.json\` checked — no changes detected." ;; error) body+=$'\n'"- ⚠️ \`device_hardware.json\` skipped — ${hardware_detail}." ;; *) body+=$'\n'"- ❓ \`device_hardware.json\` — unknown status." ;; esac # Crowdin & graphs (always attempted) body+=$'\n'"- Source strings were uploaded to Crowdin." body+=$'\n'"- Latest translations were downloaded from Crowdin (if available)." body+=$'\n'"- Updated module dependency graphs in README.md files (if changed)." body+=$'\n' body+=$'\n'"Please review the changes." # Write multi-line body to output { echo "content<> "$GITHUB_OUTPUT" - name: Create Pull Request if changes occurred uses: peter-evans/create-pull-request@v8 with: token: ${{ secrets.CROWDIN_GITHUB_TOKEN }} commit-message: | chore: Scheduled updates (Firmware, Hardware, Translations, Graphs) Automated updates for: - Firmware releases list - Device hardware list - Crowdin source string uploads - Crowdin translation downloads - Module dependency graphs title: 'chore: Scheduled updates (Firmware, Hardware, Translations, Graphs)' body: ${{ steps.pr_body.outputs.content }} branch: 'scheduled-updates' base: 'main' delete-branch: true add-paths: | androidApp/src/main/assets/firmware_releases.json androidApp/src/main/assets/device_hardware.json fastlane/metadata/android/** **/strings.xml **/README.md labels: | automation l10n firmware hardware check-workflow-status: name: Check Workflow Status runs-on: ubuntu-24.04-arm permissions: {} needs: - update_assets if: always() steps: - name: Check Workflow Status if: "contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')" run: | echo "One of the dependent jobs failed or was cancelled. Failing the workflow." exit 1