From 097c0790b84b445b488aac73535db69f3eabeb6e Mon Sep 17 00:00:00 2001 From: Anton Afanasyeu Date: Tue, 2 Jun 2026 16:08:46 +0200 Subject: [PATCH 1/2] Add gzip-aware crash upload and OTA staging build/deploy tooling. This restores the interrupted stash work on a dedicated feature branch, including backend decoding support, Dockerized build scripts, and staging channel artifacts for end-to-end OTA validation. Co-authored-by: Cursor --- .gitignore | 2 + Dockerfile | 2 + .../androidcast/crash/CrashUploadClient.java | 13 ++++- docker-compose.build.yml | 52 +++++++++++++++++++ docs/CRASH_REPORTER.md | 2 +- docs/OPEN_API.md | 2 + docs/OTA.md | 14 ++++- docs/ROADMAP.md | 1 + examples/crash_reporter/backend/README.md | 16 ++++++ examples/crash_reporter/backend/nginx.conf | 4 +- examples/crash_reporter/backend/nginx.vm.conf | 5 +- .../backend/public/api/upload.php | 7 ++- .../backend/scripts/test_gzip_upload.sh | 33 ++++++++++++ .../crash_reporter/backend/src/bootstrap.php | 19 +++++++ examples/ota/v0/ota/channel/staging.json | 4 ++ scripts/deploy-ota-staging.sh | 32 ++++++++++++ scripts/docker-build-ota.sh | 39 ++++++++++++++ scripts/generate-ota-v0.sh | 22 ++++++-- 18 files changed, 258 insertions(+), 11 deletions(-) create mode 100644 docker-compose.build.yml create mode 100755 examples/crash_reporter/backend/scripts/test_gzip_upload.sh create mode 100644 examples/ota/v0/ota/channel/staging.json create mode 100755 scripts/deploy-ota-staging.sh create mode 100755 scripts/docker-build-ota.sh diff --git a/.gitignore b/.gitignore index e9277da..d68c8f0 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ __pycache__ desktop/session-studio/build/ build/ config.php.alpine +out/ +ota-publish/ diff --git a/Dockerfile b/Dockerfile index 0db1936..c4f910f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -71,4 +71,6 @@ COPY scripts/ci-build.sh scripts/ci-build-and-publish-ota.sh scripts/generate-ot RUN chmod +x /workspace/scripts/ci-build.sh /workspace/scripts/ci-build-and-publish-ota.sh \ /workspace/scripts/generate-ota-v0.sh /workspace/scripts/build-native-codecs.sh +# Full tree is bind-mounted in CI; default command runs the pipeline. +# OTA staging publish: ./scripts/ci-build-and-publish-ota.sh (see docs/BUILD_DEPLOY.md). CMD ["./scripts/ci-build.sh"] diff --git a/app/src/main/java/com/foxx/androidcast/crash/CrashUploadClient.java b/app/src/main/java/com/foxx/androidcast/crash/CrashUploadClient.java index 6e77efd..37c5a0b 100644 --- a/app/src/main/java/com/foxx/androidcast/crash/CrashUploadClient.java +++ b/app/src/main/java/com/foxx/androidcast/crash/CrashUploadClient.java @@ -19,6 +19,7 @@ import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.util.zip.GZIPOutputStream; /** POST crash JSON to the PHP receiver. */ public final class CrashUploadClient { @@ -27,6 +28,14 @@ public final class CrashUploadClient { private CrashUploadClient() {} + private static byte[] gzip(byte[] plain) throws java.io.IOException { + ByteArrayOutputStream buf = new ByteArrayOutputStream(Math.max(256, plain.length / 4)); + try (GZIPOutputStream gz = new GZIPOutputStream(buf)) { + gz.write(plain); + } + return buf.toByteArray(); + } + public static boolean upload(String uploadUrl, JSONObject report) { if (uploadUrl == null || uploadUrl.isEmpty() || report == null) { return false; @@ -40,7 +49,9 @@ public final class CrashUploadClient { conn.setDoOutput(true); conn.setRequestProperty("Content-Type", "application/json; charset=utf-8"); conn.setRequestProperty("Accept", "application/json"); - byte[] body = report.toString().getBytes(StandardCharsets.UTF_8); + conn.setRequestProperty("Content-Encoding", "gzip"); + byte[] json = report.toString().getBytes(StandardCharsets.UTF_8); + byte[] body = gzip(json); try (OutputStream out = conn.getOutputStream()) { out.write(body); } diff --git a/docker-compose.build.yml b/docker-compose.build.yml new file mode 100644 index 0000000..bb7ad03 --- /dev/null +++ b/docker-compose.build.yml @@ -0,0 +1,52 @@ +# Android Cast — Docker build + OTA staging publish +# +# export OTA_BASE_URL=https://apps.f0xx.org +# docker compose -f docker-compose.build.yml run --rm android-build +# +# Outputs on the host: ./out/android_cast-latest.apk ./out/ota/v0/... +# +# Publish APK only from an existing build (skip compile): +# docker compose -f docker-compose.build.yml run --rm ota-publish-only + +services: + android-build: + build: + context: . + dockerfile: Dockerfile + image: androidcast-ci:latest + working_dir: /workspace + volumes: + - .:/workspace + - gradle-cache:/root/.gradle + environment: + OTA_BASE_URL: ${OTA_BASE_URL:-} + OTA_CHANNEL: ${OTA_CHANNEL:-staging} + GRADLE_TASK: ${GRADLE_TASK:-assembleDebug} + SKIP_NATIVE: ${SKIP_NATIVE:-0} + SKIP_TESTS: ${SKIP_TESTS:-0} + RELEASE_NOTES: ${RELEASE_NOTES:-} + OUT_DIR: /workspace/out + command: ["./scripts/ci-build-and-publish-ota.sh"] + + ota-publish-only: + image: androidcast-ci:latest + working_dir: /workspace + volumes: + - .:/workspace + environment: + OTA_BASE_URL: ${OTA_BASE_URL:?set OTA_BASE_URL} + OTA_CHANNEL: ${OTA_CHANNEL:-staging} + APK_PATH: ${APK_PATH:-/workspace/app/build/outputs/apk/debug/app-debug.apk} + RELEASE_NOTES: ${RELEASE_NOTES:-} + entrypoint: ["/bin/bash", "-c"] + command: + - | + set -euo pipefail + ./scripts/generate-ota-v0.sh "$${APK_PATH}" "$${OTA_BASE_URL}" /workspace/out/ota-publish "$${OTA_CHANNEL}" + mkdir -p /workspace/out/ota + cp -rf /workspace/out/ota-publish/v0 /workspace/out/ota/ + cp -f "$${APK_PATH}" /workspace/out/android_cast-latest.apk + echo "Published to /workspace/out/ota/v0/ota/channel/$${OTA_CHANNEL}.json" + +volumes: + gradle-cache: diff --git a/docs/CRASH_REPORTER.md b/docs/CRASH_REPORTER.md index 0160ce0..f6ff7d7 100644 --- a/docs/CRASH_REPORTER.md +++ b/docs/CRASH_REPORTER.md @@ -4,7 +4,7 @@ - **Main process:** `CrashReporter.install()` — any uncaught Java exception (cast or idle UI) → `files/crash_reports/pending/crash_yyyyMMdd_HHmmss.json` (same stamp as `session_*.json`). Active cast session stats are flushed with `globals.reason: "crash"` when applicable. - **Runtime context:** every report includes `runtime_context` (foreground activity, `CastActiveState`, last `session_*.json` name) and `scenario` (`cast_session` vs `app_runtime`). -- **Watcher process:** `CrashWatcherService` in `:crashwatcher` — reads `settings.json` → `crash`, uploads pending reports, ingests native stub files. +- **Watcher process:** `CrashWatcherService` in `:crashwatcher` — reads `settings.json` → `crash`, uploads pending reports (JSON body gzip-compressed), ingests native stub files. - **Native:** `crash_hook.c` (unwind + `dladdr`) writes stubs to `crash_reports/native/`; watcher converts to schema v1 JSON. - **User toggle:** Settings drawer → **Send anonymous crash logs** (`AppPreferences.send_anonymous_crash_logs`, default on). diff --git a/docs/OPEN_API.md b/docs/OPEN_API.md index da1c4fc..1b244d2 100644 --- a/docs/OPEN_API.md +++ b/docs/OPEN_API.md @@ -27,6 +27,8 @@ Store in DB: ## Upload API (`POST …/api/upload.php`) +Clients may send **`Content-Encoding: gzip`** with a gzip-compressed JSON body (same schema). The BE nginx upload `location` should use `gunzip on` + `gunzip_types application/json`; PHP accepts plain or gzip. + Phases: 1. **Phase A (now):** Accept extra JSON fields; persist inside `payload_json`; console search includes `ingest.source` in blob. diff --git a/docs/OTA.md b/docs/OTA.md index 4c215b3..9a1768d 100644 --- a/docs/OTA.md +++ b/docs/OTA.md @@ -111,10 +111,20 @@ When `ota.trusted=true` and build is debug, strict checksum/sign enforcement is ```bash chmod +x scripts/generate-ota-v0.sh -./scripts/generate-ota-v0.sh app/build/outputs/apk/release/app-release.apk https://your-host:port ./ota-publish +./scripts/generate-ota-v0.sh app/build/outputs/apk/release/app-release.apk https://your-host:port ./ota-publish staging ``` -Upload the contents of `ota-publish/` to your web root so `https://your-host/v0/ota/...` resolves. +Fourth argument is the **channel** file name (`stable`, `staging`, `next`, …). Upload `ota-publish/v0/` to your web root so `https://your-host/v0/ota/...` resolves. + +### Docker build + staging channel + +See [BUILD_DEPLOY.md](BUILD_DEPLOY.md): + +```bash +export OTA_BASE_URL=https://apps.f0xx.org +./scripts/docker-build-ota.sh +./scripts/deploy-ota-staging.sh # after setting OTA_DEPLOY_TARGET +``` ## App configuration diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 67116a8..4e4fcda 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -168,4 +168,5 @@ See [ndk/README.md](../ndk/README.md). - [PRO_AAR.md](PRO_AAR.md) — cast-core / cast-pro entitlements - [OPEN_API.md](OPEN_API.md) — crash ingest triage API - [OTA.md](OTA.md) — backend layout v0 +- [BUILD_DEPLOY.md](BUILD_DEPLOY.md) — Docker build, staging OTA, deploy flow - [CRASH_REPORTER.md](CRASH_REPORTER.md) — crash upload diff --git a/examples/crash_reporter/backend/README.md b/examples/crash_reporter/backend/README.md index b0fbf0c..87954f5 100644 --- a/examples/crash_reporter/backend/README.md +++ b/examples/crash_reporter/backend/README.md @@ -109,6 +109,22 @@ curl -X POST http://127.0.0.1:8080/api/upload.php \ -d @../sample_crash_java.json ``` +**Gzip ingest** (Android sends `Content-Encoding: gzip`; plain JSON still works): + +```bash +./scripts/test_gzip_upload.sh +# BASE=https://apps.f0xx.org/app/androidcast_project/crashes ./scripts/test_gzip_upload.sh +``` + +On the **BE nginx** upload `location`, enable gunzip so PHP receives plain JSON (see `nginx.vm.conf`): + +```nginx +gunzip on; +gunzip_types application/json; +``` + +PHP also decodes gzip if the header reaches FPM (`read_crash_upload_body()` in `bootstrap.php`). After deploy, sync `public/` + `src/` and reload nginx. + ## Production (nginx + PHP-FPM) **Deploy topology (FE/BE, ports, sshfs):** [docs/INFRA.md](../../../docs/INFRA.md). diff --git a/examples/crash_reporter/backend/nginx.conf b/examples/crash_reporter/backend/nginx.conf index 444eeed..2ee1ef1 100644 --- a/examples/crash_reporter/backend/nginx.conf +++ b/examples/crash_reporter/backend/nginx.conf @@ -17,11 +17,13 @@ server { } location ~ ^/app/androidcast_project/crashes/api/upload\.php$ { + gunzip on; + gunzip_types application/json; alias /var/www/androidcast_crashes/public/api/upload.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME /var/www/androidcast_crashes/public/api/upload.php; fastcgi_pass unix:/run/php-fpm/www.sock; - client_max_body_size 4m; + client_max_body_size 8m; } location ~ ^/app/androidcast_project/crashes/.+\.php$ { diff --git a/examples/crash_reporter/backend/nginx.vm.conf b/examples/crash_reporter/backend/nginx.vm.conf index b8c1c54..ea21ee8 100644 --- a/examples/crash_reporter/backend/nginx.vm.conf +++ b/examples/crash_reporter/backend/nginx.vm.conf @@ -36,12 +36,15 @@ server { } location = /app/androidcast_project/crashes/api/upload.php { + # Decompress gzip uploads from Android before PHP (needs ngx_http_gunzip_module). + gunzip on; + gunzip_types application/json; include fastcgi_params; fastcgi_pass unix:/run/php-fpm.socket; fastcgi_param SCRIPT_FILENAME /var/www/localhost/htdocs/apps/app/androidcast_project/android_cast/examples/crash_reporter/backend/public/api/upload.php; fastcgi_param SCRIPT_NAME /app/androidcast_project/crashes/api/upload.php; fastcgi_param REQUEST_URI $request_uri; - client_max_body_size 4m; + client_max_body_size 8m; } location ^~ /app/androidcast_project/crashes/ { diff --git a/examples/crash_reporter/backend/public/api/upload.php b/examples/crash_reporter/backend/public/api/upload.php index 84fb233..adf85b8 100644 --- a/examples/crash_reporter/backend/public/api/upload.php +++ b/examples/crash_reporter/backend/public/api/upload.php @@ -13,10 +13,13 @@ declare(strict_types=1); require_once __DIR__ . '/../../src/bootstrap.php'; -$raw = file_get_contents('php://input'); -if ($raw === false || $raw === '') { +$raw = read_crash_upload_body(); +if ($raw === null) { json_out(['ok' => false, 'error' => 'empty body'], 400); } +if ($raw === false) { + json_out(['ok' => false, 'error' => 'invalid gzip body'], 400); +} $data = json_decode($raw, true); if (!is_array($data)) { json_out(['ok' => false, 'error' => 'invalid json'], 400); diff --git a/examples/crash_reporter/backend/scripts/test_gzip_upload.sh b/examples/crash_reporter/backend/scripts/test_gzip_upload.sh new file mode 100755 index 0000000..836aa73 --- /dev/null +++ b/examples/crash_reporter/backend/scripts/test_gzip_upload.sh @@ -0,0 +1,33 @@ +#!/bin/sh +# Smoke-test gzip crash upload (plain JSON still accepted). +# Usage: BASE=http://127.0.0.1:8080/app/androidcast_project/crashes ./scripts/test_gzip_upload.sh +set -eu + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +SAMPLE="${ROOT}/../sample_crash_java.json" +BASE="${BASE:-http://127.0.0.1:8080/app/androidcast_project/crashes}" +URL="${BASE}/api/upload.php" +RID="gzip-test-$(date +%s)" + +if [ ! -f "$SAMPLE" ]; then + echo "Missing $SAMPLE" >&2 + exit 1 +fi + +BODY="$(mktemp)" +jq --arg rid "$RID" '.report_id = $rid' "$SAMPLE" > "$BODY" +GZIP="$(mktemp)" +gzip -c -f "$BODY" > "$GZIP" + +echo "POST plain JSON..." +curl -sS -o /dev/null -w "plain:%{http_code}\n" -X POST "$URL" \ + -H 'Content-Type: application/json' \ + --data-binary @"$BODY" + +echo "POST gzip (Content-Encoding: gzip)..." +curl -sS -w "\n%{http_code}\n" -X POST "$URL" \ + -H 'Content-Type: application/json' \ + -H 'Content-Encoding: gzip' \ + --data-binary @"$GZIP" + +rm -f "$BODY" "$GZIP" diff --git a/examples/crash_reporter/backend/src/bootstrap.php b/examples/crash_reporter/backend/src/bootstrap.php index 1b068f2..1b33fc2 100644 --- a/examples/crash_reporter/backend/src/bootstrap.php +++ b/examples/crash_reporter/backend/src/bootstrap.php @@ -79,6 +79,25 @@ function json_out(array $data, int $code = 200): void { exit; } +/** + * Raw POST body for crash ingest. Supports plain JSON or Content-Encoding: gzip + * (when nginx gunzip is off or the client talks to PHP directly). + * + * @return string|null decoded JSON text, null if empty, false if gzip decode failed + */ +function read_crash_upload_body(): string|false|null { + $raw = file_get_contents('php://input'); + if ($raw === false || $raw === '') { + return null; + } + $encoding = $_SERVER['HTTP_CONTENT_ENCODING'] ?? ''; + if ($encoding !== '' && stripos($encoding, 'gzip') !== false) { + $decoded = @gzdecode($raw); + return $decoded === false ? false : $decoded; + } + return $raw; +} + /** Short lines for expandable list preview (not full report detail). */ function report_brief_lines(array $row, bool $grouped): array { if ($grouped) { diff --git a/examples/ota/v0/ota/channel/staging.json b/examples/ota/v0/ota/channel/staging.json new file mode 100644 index 0000000..5361adc --- /dev/null +++ b/examples/ota/v0/ota/channel/staging.json @@ -0,0 +1,4 @@ +{ + "schema": "v0", + "manifestUrl": "https://apps.f0xx.org/v0/ota/00/00.01/00.01.00/android_cast_00.01.00.00_manifest.json" +} diff --git a/scripts/deploy-ota-staging.sh b/scripts/deploy-ota-staging.sh new file mode 100755 index 0000000..ea01636 --- /dev/null +++ b/scripts/deploy-ota-staging.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# Rsync ./out/ota/v0 to a remote web root (staging OTA tree). +# +# Usage: +# OTA_DEPLOY_TARGET=foxx@10.7.16.128:/var/www/localhost/htdocs/ \ +# ./scripts/deploy-ota-staging.sh +# +# Dry run: +# OTA_DEPLOY_DRY_RUN=1 ./scripts/deploy-ota-staging.sh +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +SRC="${ROOT}/out/ota/v0" +TARGET="${OTA_DEPLOY_TARGET:?Set OTA_DEPLOY_TARGET=user@host:/path/to/web/root}" + +if [[ ! -d "$SRC" ]]; then + echo "Missing ${SRC} — run ./scripts/docker-build-ota.sh first" >&2 + exit 1 +fi + +RSYNC_OPTS=(-av --delete) +if [[ "${OTA_DEPLOY_DRY_RUN:-}" == "1" ]]; then + RSYNC_OPTS+=(--dry-run -v) +fi + +echo "Sync ${SRC}/ -> ${TARGET}/v0/" +rsync "${RSYNC_OPTS[@]}" "${SRC}/" "${TARGET%/}/v0/" + +echo "Channel pointer (after deploy):" +if [[ -f "${ROOT}/out/BUILD_INFO.json" ]]; then + grep channelUrl "${ROOT}/out/BUILD_INFO.json" || true +fi diff --git a/scripts/docker-build-ota.sh b/scripts/docker-build-ota.sh new file mode 100755 index 0000000..6a9dc13 --- /dev/null +++ b/scripts/docker-build-ota.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Host wrapper: build androidcast-ci image and run build + OTA staging publish. +# +# Usage: +# export OTA_BASE_URL=https://apps.f0xx.org +# ./scripts/docker-build-ota.sh +# +# Artifacts: ./out/android_cast-latest.apk ./out/ota/v0/ota/... +# +# Deploy to BE (example — adjust host/path): +# rsync -av ./out/ota/v0/ user@be:/var/www/localhost/htdocs/v0/ +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +IMAGE="${ANDROIDCAST_CI_IMAGE:-androidcast-ci:latest}" + +docker build -t "$IMAGE" -f "${ROOT}/Dockerfile" "${ROOT}" + +mkdir -p "${ROOT}/out" + +docker run --rm \ + -v "${ROOT}:/workspace" \ + -w /workspace \ + -e OTA_BASE_URL="${OTA_BASE_URL:-}" \ + -e OTA_CHANNEL="${OTA_CHANNEL:-staging}" \ + -e GRADLE_TASK="${GRADLE_TASK:-assembleDebug}" \ + -e SKIP_NATIVE="${SKIP_NATIVE:-0}" \ + -e SKIP_TESTS="${SKIP_TESTS:-0}" \ + -e RELEASE_NOTES="${RELEASE_NOTES:-}" \ + -e OUT_DIR=/workspace/out \ + "$IMAGE" \ + ./scripts/ci-build-and-publish-ota.sh + +echo "" +echo "Done. APK: ${ROOT}/out/android_cast-latest.apk" +if [[ -f "${ROOT}/out/BUILD_INFO.json" ]]; then + echo "OTA channel URL:" + sed -n 's/.*"channelUrl": "\([^"]*\)".*/\1/p' "${ROOT}/out/BUILD_INFO.json" | head -1 +fi diff --git a/scripts/generate-ota-v0.sh b/scripts/generate-ota-v0.sh index d8035ea..95b14a3 100755 --- a/scripts/generate-ota-v0.sh +++ b/scripts/generate-ota-v0.sh @@ -4,9 +4,12 @@ # Usage: # ./scripts/generate-ota-v0.sh path/to/app-release.apk https://host[:port] [out-dir] [channel] # +# channel — JSON file under v0/ota/channel/ (default: stable). Use staging for QA. +# RELEASE_NOTES — optional env, embedded in manifest. +# # channel — JSON under v0/ota/channel/ (stable, staging, dev, nightly, or custom) # Writes under out-dir (default: ./ota-publish): -# v0/ota/channel/stable.json +# v0/ota/channel/.json # v0/ota/00/00.MM/00.MM.mm/android_cast_00.MM.mm.BB_manifest.json # v0/ota/00/00.MM/00.MM.mm/android_cast_00.MM.mm.BB_sign.json # v0/ota/00/00.MM/00.MM.mm/android_cast_00.MM.mm.BB.otapkg @@ -17,6 +20,19 @@ apk="${1:?APK path required}" host_base="${2:?Base URL required, e.g. https://192.168.1.1:8080}" out_dir="${3:-ota-publish}" channel="${4:-stable}" +release_notes="${RELEASE_NOTES:-}" +json_notes() { + if command -v jq >/dev/null 2>&1; then + jq -n --arg n "$release_notes" '$n' + else + local esc="${release_notes//\\/\\\\}" + esc="${esc//\"/\\\"}" + esc="${esc//$'\n'/\\n}" + esc="${esc//$'\r'/}" + printf '"%s"' "$esc" + fi +} +NOTES_JSON="$(json_notes)" if [[ ! -f "$apk" ]]; then echo "APK not found: $apk" >&2 @@ -84,7 +100,7 @@ cat >"${rel_dir}/${manifest_name}" <"${rel_dir}/${manifest_name}" < Date: Tue, 2 Jun 2026 17:37:32 +0200 Subject: [PATCH 2/2] Fix Docker CI and orchestration deploy reliability for full rebuild flows. Use JDK 17 and make in the CI image, avoid self-overwriting DB init SQL, and make Gradle/native builds use container-local SDK paths and CMake caches. Co-authored-by: Cursor --- Dockerfile | 6 +++--- orchestration/deploy.sh | 27 +++++++++++++++------------ scripts/build-native-codecs.sh | 4 +++- scripts/ci-build-and-publish-ota.sh | 23 ++++++++++++++++++++--- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/Dockerfile b/Dockerfile index c4f910f..9183a6a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,11 +35,11 @@ ENV ANDROID_SDK_ROOT="${ANDROID_SDK_ROOT}" \ ANDROIDCAST_USE_DISTCC=0 \ ANDROIDCAST_CI_VERSION="${ANDROIDCAST_CI_VERSION}" \ GRADLE_USER_HOME=/root/.gradle \ - JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 + JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 RUN apt-get update && apt-get install -y --no-install-recommends \ - bash ca-certificates curl git openjdk-21-jdk-headless unzip wget \ - ninja-build cmake ccache \ + bash ca-certificates curl git openjdk-17-jdk-headless unzip wget \ + make ninja-build cmake ccache \ && rm -rf /var/lib/apt/lists/* RUN apt-get update && apt-get install -y --no-install-recommends distcc \ diff --git a/orchestration/deploy.sh b/orchestration/deploy.sh index 7394b38..1a653d5 100755 --- a/orchestration/deploy.sh +++ b/orchestration/deploy.sh @@ -111,18 +111,21 @@ return [ EOF cp "$SRC_SCHEMA" "$DB_INIT_DIR/010_schema.sql" -{ - echo "USE \`${MARIADB_DATABASE}\`;" - cat "$SRC_MIG_004" -} >"$DB_INIT_DIR/040_ticket_workflow.sql" -{ - echo "USE \`${MARIADB_DATABASE}\`;" - cat "$SRC_MIG_005" -} >"$DB_INIT_DIR/050_ticket_attachments.sql" -{ - echo "USE \`${MARIADB_DATABASE}\`;" - cat "$SRC_MIG_060" -} >"$DB_INIT_DIR/060_build_ecosystem.sql" +write_migration_with_db() { + local src="$1" + local dest="$2" + local tmp + tmp="$(mktemp)" + { + echo "USE \`${MARIADB_DATABASE}\`;" + cat "$src" + } >"$tmp" + mv "$tmp" "$dest" +} + +write_migration_with_db "$SRC_MIG_004" "$DB_INIT_DIR/040_ticket_workflow.sql" +write_migration_with_db "$SRC_MIG_005" "$DB_INIT_DIR/050_ticket_attachments.sql" +write_migration_with_db "$SRC_MIG_060" "$DB_INIT_DIR/060_build_ecosystem.sql" echo "Pulling images..." docker compose -f "$ROOT_DIR/docker-compose.yml" pull landing-fe gitea-fe crashes-fe build-fe mariadb gitea || true diff --git a/scripts/build-native-codecs.sh b/scripts/build-native-codecs.sh index efc441c..64ca2ac 100755 --- a/scripts/build-native-codecs.sh +++ b/scripts/build-native-codecs.sh @@ -222,7 +222,9 @@ if [[ ! -f "$LIBVPX_A" ]]; then fi cp -f "$LIBVPX_A" "$OUT/libvpx.a" -cp -f "$OUT/libvpx.a" "$PROJECT_OUT/libvpx.a" +if [[ "$OUT" != "$PROJECT_OUT" ]]; then + cp -f "$OUT/libvpx.a" "$PROJECT_OUT/libvpx.a" +fi echo "" echo "Built: $PROJECT_OUT/libvpx.a (size $(ls -lah "${PROJECT_OUT}/libvpx.a" | awk '{print $5}'))" diff --git a/scripts/ci-build-and-publish-ota.sh b/scripts/ci-build-and-publish-ota.sh index 6db1170..1a30b6a 100755 --- a/scripts/ci-build-and-publish-ota.sh +++ b/scripts/ci-build-and-publish-ota.sh @@ -42,10 +42,24 @@ fi export ANDROIDCAST_ROOT="$ROOT" export ANDROIDCAST_CCACHE="${ANDROIDCAST_CCACHE:-1}" +GRADLE_JAVA_OPT=() +if [[ -n "${JAVA_HOME:-}" ]]; then + GRADLE_JAVA_OPT=(-Dorg.gradle.java.home="${JAVA_HOME}") +fi # shellcheck source=android-ndk.sh source "$ROOT/scripts/android-ndk.sh" export ANDROID_NDK_HOME="$(androidcast_find_ndk_root "$ROOT")" export ANDROID_NDK_ROOT="$ANDROID_NDK_HOME" +if [[ -n "${ANDROID_SDK_ROOT:-}" ]]; then + LOCAL_PROPS="$ROOT/local.properties" + LOCAL_PROPS_BACKUP="" + if [[ -f "$LOCAL_PROPS" ]]; then + LOCAL_PROPS_BACKUP="$(mktemp)" + cp -f "$LOCAL_PROPS" "$LOCAL_PROPS_BACKUP" + fi + trap 'if [[ -n "${LOCAL_PROPS_BACKUP:-}" && -f "${LOCAL_PROPS_BACKUP}" ]]; then cp -f "${LOCAL_PROPS_BACKUP}" "$LOCAL_PROPS"; rm -f "${LOCAL_PROPS_BACKUP}"; else rm -f "$LOCAL_PROPS"; fi' EXIT + printf 'sdk.dir=%s\n' "${ANDROID_SDK_ROOT//\\/\\\\}" > "$LOCAL_PROPS" +fi if [[ "$RUN_NATIVE" == "1" && "$RUN_APK" == "1" ]]; then echo "==> Phase: native codecs" @@ -59,16 +73,19 @@ else echo "==> Phase: native codecs (skipped)" fi +# Avoid host-specific CMake cache paths when running in Docker. +rm -rf "$ROOT/app/.cxx" "$ROOT/app/build/.cxx" "$ROOT/app/build/intermediates/cxx" + if [[ "$RUN_APK" == "1" ]]; then echo "==> Phase: gradle ${GRADLE_TASK}" if [[ "$RUN_UNIT_TESTS" == "1" ]]; then - ./gradlew --no-daemon clean "${GRADLE_TASK}" testDebugUnitTest + ./gradlew --no-daemon "${GRADLE_JAVA_OPT[@]}" clean "${GRADLE_TASK}" testDebugUnitTest else - ./gradlew --no-daemon clean "${GRADLE_TASK}" + ./gradlew --no-daemon "${GRADLE_JAVA_OPT[@]}" clean "${GRADLE_TASK}" fi else echo "==> Phase: gradle tests-only" - ./gradlew --no-daemon testDebugUnitTest + ./gradlew --no-daemon "${GRADLE_JAVA_OPT[@]}" testDebugUnitTest fi APK=""