#!/usr/bin/env bash # Run Android wallet smoke against several build / published variants. # # Default order: # 1) stable-self — **only** self-build of current published stable (WARN) # 2) published — current F-Droid APK # 3) older — F-Droid APKs ~3/6/9/12 mo if still hosted (WARN, no self-build) # 4) master — tip self-build (WARN) # # Self-build policy: current stable + master/fix only — never rebuild old releases. # # Severity: age ≤14d → BLOCKER; milestones/older/self-builds → WARN # # Env: # STABLE_TAG, STABLE_APK_URL, STABLE_APK_NAME # MASTER_BRANCH, FIX_BRANCH # VARIANTS default: stable-self,published,older,master # INCLUDE_OLDER=1 expand token "older" / "milestones" (default 1) # MILESTONE_DAYS default "90 180 270 365" (≈ 3,6,9,12 months) # MILESTONE_TOLERANCE_DAYS default 50 — max distance to accept a tag # BLOCKER_MAX_AGE_DAYS default 14 # PUBLISHED_FORCE_BLOCKER=1 always treat current published as blocker # FAIL_FAST=1, GUI=0, STACK=… # set -euo pipefail ROOT=$(cd "$(dirname "$0")" && pwd) # shellcheck source=lib_android_env.sh . "$ROOT/lib_android_env.sh" # shellcheck source=lib_release_age.sh . "$ROOT/lib_release_age.sh" : "${STABLE_TAG:=wallet-1.6.1}" : "${STABLE_APK_URL:=https://f-droid.org/repo/net.taler.wallet.fdroid_854.apk}" : "${STABLE_APK_NAME:=net.taler.wallet.fdroid_854.apk}" : "${MASTER_BRANCH:=master}" : "${FIX_BRANCH:=dev/hernani-inference/fix-bank-withdraw-auto-exchange}" : "${VARIANTS:=stable-self,published,older,master}" : "${INCLUDE_OLDER:=1}" : "${MILESTONE_DAYS:=90 180 270 365}" : "${MILESTONE_TOLERANCE_DAYS:=50}" : "${BLOCKER_MAX_AGE_DAYS:=14}" : "${PUBLISHED_FORCE_BLOCKER:=0}" : "${FAIL_FAST:=1}" : "${GUI:=0}" : "${STACK:=auto}" : "${TALER_ANDROID_SRC:=$HOME/taler/taler-android}" export TALER_ANDROID_SRC BLOCKER_MAX_AGE_DAYS MILESTONE_DAYS MILESTONE_TOLERANCE_DAYS TS=$(date +%Y%m%d-%H%M%S) MATRIX_OUT="${MATRIX_OUT:-$ROOT/out-matrix-$TS}" mkdir -p "$MATRIX_OUT" SUMMARY="$MATRIX_OUT/SUMMARY.txt" : >"$SUMMARY" echo "=== Android variant matrix ===" | tee -a "$SUMMARY" echo "STACK=$STACK GUI=$GUI headless=${EMULATOR_HEADLESS} variants=$VARIANTS" | tee -a "$SUMMARY" echo "STABLE_TAG=$STABLE_TAG BLOCKER_MAX_AGE_DAYS=$BLOCKER_MAX_AGE_DAYS" | tee -a "$SUMMARY" echo "milestones(days)=$MILESTONE_DAYS tolerance=$MILESTONE_TOLERANCE_DAYS (always WARN)" | tee -a "$SUMMARY" echo "out: $MATRIX_OUT" | tee -a "$SUMMARY" android_ensure_device || { echo "no adb device — start ./start-android-emulator.sh --wait" >&2 exit 3 } export SERIAL ANDROID_SERIAL echo "device: $SERIAL" | tee -a "$SUMMARY" # --- expand VARIANTS (token "older" → concrete older-pub / older-self entries) --- # Append one older release as WARN-only — **F-Droid APK only** (no self-build). # Returns 0 if queued, 1 if skipped (no published APK). append_older_warn() { local t="$1" age="$2" target="${3:-}" local vn meta apk_vc apk_url vn="${t#wallet-}" if [ -n "$target" ]; then echo " milestone ~${target}d → $t (age=${age}d) severity=WARN" | tee -a "$SUMMARY" else echo " older $t (age=${age}d) severity=WARN" | tee -a "$SUMMARY" fi meta=$(fdroid_apk_for_version_name "$vn" 2>/dev/null || true) if [ -n "$meta" ]; then apk_vc="${meta%%|*}" apk_url="${meta#*|}" if fdroid_apk_exists "$apk_url"; then out+=("older-pub:${vn}:${apk_vc}:warn") return 0 fi echo " SKIP $t — no F-Droid APK at ${apk_url:-?} (self-build only for current stable)" | tee -a "$SUMMARY" else echo " SKIP $t — cannot map F-Droid versionCode (self-build only for current stable)" | tee -a "$SUMMARY" fi return 1 } expand_variants() { local raw="$1" local -a out=() local v tag vn age sev line t target IFS=',' read -r -a toks <<<"$raw" for v in "${toks[@]}"; do v=$(echo "$v" | tr -d '[:space:]') [ -n "$v" ] || continue if [ "$v" = "older" ] || [ "$v" = "older-releases" ] || [ "$v" = "milestones" ]; then [ "$INCLUDE_OLDER" = "1" ] || continue # 3/6/9/12-month samples: published APK only, always WARN. local found=0 queued=0 while read -r t age target; do [ -n "$t" ] || continue [ "$t" = "$STABLE_TAG" ] && continue found=1 if append_older_warn "$t" "$age" "$target"; then queued=$((queued + 1)) fi done < <(select_milestone_tags "$STABLE_TAG" 2>/dev/null || true) if [ "$found" = "0" ]; then echo " (no milestone tags within ±${MILESTONE_TOLERANCE_DAYS}d of $MILESTONE_DAYS)" | tee -a "$SUMMARY" elif [ "$queued" = "0" ]; then echo " (milestone tags found but none still on F-Droid — no older APK smokes)" | tee -a "$SUMMARY" fi else out+=("$v") fi done printf '%s\n' "${out[@]}" } mapfile -t VLIST < <(expand_variants "$VARIANTS") echo "expanded variants: ${VLIST[*]}" | tee -a "$SUMMARY" declare -a RESULTS=() declare -a WARNINGS=() overall=0 severity_for_published_current() { if [ "${PUBLISHED_FORCE_BLOCKER}" = "1" ]; then echo blocker return fi local days days=$(release_age_days "$STABLE_TAG" 2>/dev/null || true) severity_from_age_days "$days" } run_published_apk() { local label="$1" local apk_url="$2" local apk_name="$3" local sev="$4" local out="$MATRIX_OUT/$label" mkdir -p "$out" echo "" | tee -a "$SUMMARY" echo "-------- variant: $label (F-Droid APK) [$(echo "$sev" | tr 'a-z' 'A-Z')] --------" | tee -a "$SUMMARY" echo " url=$apk_url" | tee -a "$SUMMARY" local smoke="$ROOT/run-android-pay-smoke.sh" [ "$GUI" = "1" ] && smoke="$ROOT/run-android-gui-smoke.sh" local strict=1 [ "$sev" = "warn" ] && strict="${SMOKE_STRICT_WARN:-0}" [ "$sev" = "blocker" ] && strict="${SMOKE_STRICT:-1}" set +e env STACK="$STACK" \ APK_URL="$apk_url" \ APK_NAME="$apk_name" \ APK_PATH="" \ PKG=net.taler.wallet.fdroid \ OUT_DIR="$out" \ SERIAL="$SERIAL" \ AUTO_START_EMULATOR=0 \ SMOKE_STRICT="$strict" \ "$smoke" local rc=$? set -e echo "$label exit=$rc severity=$sev" | tee -a "$SUMMARY" echo "$label $rc $sev $apk_url" >>"$MATRIX_OUT/results.tsv" return "$rc" } run_selfbuild() { local label="$1" local ref="$2" local sev="${3:-warn}" local out="$MATRIX_OUT/$label" mkdir -p "$out" echo "" | tee -a "$SUMMARY" echo "-------- variant: $label (self-build ref=$ref) [$(echo "$sev" | tr 'a-z' 'A-Z')] --------" | tee -a "$SUMMARY" local strict="${SMOKE_STRICT:-1}" [ "$sev" = "warn" ] && strict="${SMOKE_STRICT_SELF:-0}" [ "$sev" = "blocker" ] && strict="${SMOKE_STRICT:-1}" set +e env STACK="$STACK" \ BRANCH="$ref" \ LABEL="$label" \ GUI="$GUI" \ OUT_DIR="$out" \ SERIAL="$SERIAL" \ ANDROID_SERIAL="$SERIAL" \ AUTO_START_EMULATOR=0 \ PKG=net.taler.wallet.fdroid.debug \ SMOKE_STRICT="$strict" \ TALER_ANDROID_SRC="$TALER_ANDROID_SRC" \ "$ROOT/run-android-build-and-smoke.sh" local rc=$? set -e echo "$label (ref=$ref) exit=$rc severity=$sev" | tee -a "$SUMMARY" echo "$label $rc $ref $sev" >>"$MATRIX_OUT/results.tsv" if [ -f "$out/build-meta.txt" ]; then echo "build-meta:" | tee -a "$SUMMARY" cat "$out/build-meta.txt" | tee -a "$SUMMARY" fi return "$rc" } record_result() { local v="$1" rc="$2" sev="$3" if [ "$rc" -eq 0 ]; then RESULTS+=("$v:OK($sev)") return fi case "$sev" in warn) echo "WARN: $v failed rc=$rc — not a blocker; continuing" | tee -a "$SUMMARY" WARNINGS+=("$v:$rc") RESULTS+=("$v:WARN($rc)") ;; blocker) echo "BLOCKER: $v failed rc=$rc (release within ${BLOCKER_MAX_AGE_DAYS}d window)" | tee -a "$SUMMARY" RESULTS+=("$v:BLOCKER($rc)") overall=1 if [ "$FAIL_FAST" = "1" ]; then echo "FAIL_FAST: stopping after blocker $v" | tee -a "$SUMMARY" return 2 fi ;; soft) echo "FAIL: $v rc=$rc" | tee -a "$SUMMARY" RESULTS+=("$v:FAIL($rc)") overall=1 if [ "$FAIL_FAST" = "1" ]; then echo "FAIL_FAST: stopping after $v" | tee -a "$SUMMARY" return 2 fi ;; esac return 0 } for raw in "${VLIST[@]}"; do v=$(echo "$raw" | tr -d '[:space:]') [ -n "$v" ] || continue rc=0 sev=warn stop=0 case "$v" in published|fdroid|stable-published) sev=$(severity_for_published_current) age=$(release_age_days "$STABLE_TAG" 2>/dev/null || echo "?") echo "published current: tag=$STABLE_TAG age_days=$age → severity=$sev" | tee -a "$SUMMARY" run_published_apk "published" "$STABLE_APK_URL" "$STABLE_APK_NAME" "$sev" || rc=$? ;; stable-self|stable|self-stable|release-self) # rebuild of stable: always warn (toolchain), age does not make self-build a blocker sev=warn run_selfbuild "stable-self" "$STABLE_TAG" warn || rc=$? ;; master|main) sev=warn run_selfbuild "master" "$MASTER_BRANCH" warn || rc=$? ;; fix|fix-branch|inference) sev=soft run_selfbuild "fix" "$FIX_BRANCH" soft || rc=$? ;; older-pub:*) # older-pub:VERSION:VC:SEV IFS=':' read -r _ vn apk_vc sev <<<"$v" : "${sev:=warn}" label="older-pub-${vn}" url="https://f-droid.org/repo/net.taler.wallet.fdroid_${apk_vc}.apk" name="net.taler.wallet.fdroid_${apk_vc}.apk" age=$(release_age_days "wallet-${vn}" 2>/dev/null || echo "?") echo "older published $vn age_days=$age severity=$sev" | tee -a "$SUMMARY" run_published_apk "$label" "$url" "$name" "$sev" || rc=$? v="$label" ;; older-self:*) # Explicit older-self is disabled by policy (self-build = current stable only). echo "SKIP $v — self-build only for current stable ($STABLE_TAG); use older-pub or published" | tee -a "$SUMMARY" RESULTS+=("$v:SKIP(no-selfbuild-older)") continue ;; *) # raw git ref: only master-like / fix via named tokens; bare wallet-* tags → no self-build if [[ "$v" =~ ^wallet-[0-9] ]]; then echo "SKIP self-build $v — only current stable self-build ($STABLE_TAG) is enabled" | tee -a "$SUMMARY" RESULTS+=("$v:SKIP(no-selfbuild-older)") continue fi sev=warn run_selfbuild "ref-${v//\//-}" "$v" warn || rc=$? ;; esac record_result "$v" "$rc" "$sev" || stop=$? [ "$stop" = "2" ] && break done echo "" | tee -a "$SUMMARY" echo "=== matrix done (overall=$overall) ===" | tee -a "$SUMMARY" echo "rule: age ≤ ${BLOCKER_MAX_AGE_DAYS}d → BLOCKER; 3/6/9/12mo milestones + older → WARN only" | tee -a "$SUMMARY" for r in "${RESULTS[@]}"; do echo " $r" | tee -a "$SUMMARY" done if [ "${#WARNINGS[@]}" -gt 0 ]; then echo "warnings (non-blocking):" | tee -a "$SUMMARY" for w in "${WARNINGS[@]}"; do echo " WARN $w" | tee -a "$SUMMARY" done fi echo "" echo "Artifacts: $MATRIX_OUT" exit "$overall"