Skip to content

Beta release

Beta release #34

Workflow file for this run

name: Beta release
# Publie une PRE-RELEASE GitHub consommee par le canal beta de l'application.
#
# Difference avec "Test build" : ce workflow PUBLIE, donc les APK arrivent en OTA chez les
# testeurs qui ont coche « Canal de mise a jour beta ». Ne le declencher que sur un etat du
# code qu'on accepte de diffuser.
#
# Le numero de version installe DOIT correspondre au tag, sinon la mise a jour est reproposee
# en boucle : d'ou -Pmg4.versionSuffix, qui injecte le suffixe dans le versionName de l'APK.
#
# La release stable (release.yml) reste prioritaire : a numero egal, une pre-release est
# consideree ANTERIEURE (precedence semver), donc la stable remplace toujours la derniere beta.
on:
workflow_dispatch:
inputs:
label:
description: "Identifiant de la beta (ex: beta1, rc2)"
type: string
default: beta
notes:
description: "Ce qu'il faut tester / ce qui a change"
type: string
default: ""
keep:
description: "Nombre de betas a conserver (1 = seulement celle-ci ; 0 = ne rien supprimer)"
type: string
default: "3"
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
env:
MG4_KEYSTORE_BASE64: ${{ secrets.MG4_KEYSTORE_BASE64 }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Decode platform keystore
run: |
if [ -z "$MG4_KEYSTORE_BASE64" ]; then
echo "::error::MG4_KEYSTORE_BASE64 absent. Une beta NON signee par la cle plateforme"
echo "::error::serait refusee a l'installation par le controle de signature (T-901)."
exit 1
fi
printf '%s' "$MG4_KEYSTORE_BASE64" | tr -d '[:space:]' | base64 -d > "$RUNNER_TEMP/platform.keystore"
if [ "$(head -c1 "$RUNNER_TEMP/platform.keystore" | od -An -tx1 | tr -d ' ')" != "30" ]; then
echo "::error::MG4_KEYSTORE_BASE64 est invalide. Recreer le secret avec : base64 -w0 platform.keystore"
exit 1
fi
echo "MG4_KEYSTORE=$RUNNER_TEMP/platform.keystore" >> "$GITHUB_ENV"
# Suffixe unique et croissant : le numero de run garantit qu'une beta publiee apres une
# autre est bien vue comme posterieure, meme si le libelle saisi est identique.
- name: Compute version
id: ver
run: |
# sed plutot que grep -oP : PCRE depend de la locale du runner, sed non.
BASE=$(sed -n 's/.*versionName = "\([^"]*\)".*/\1/p' app/build.gradle.kts | head -1)
if [ -z "$BASE" ]; then
echo "::error::versionName introuvable dans app/build.gradle.kts"; exit 1
fi
SUFFIX="-${{ inputs.label }}.${{ github.run_number }}"
echo "base=$BASE" >> "$GITHUB_OUTPUT"
echo "suffix=$SUFFIX" >> "$GITHUB_OUTPUT"
echo "full=${BASE}${SUFFIX}" >> "$GITHUB_OUTPUT"
echo "Version beta : ${BASE}${SUFFIX}"
- name: Build online + offline APKs
env:
MG4_KEYSTORE_PASSWORD: ${{ secrets.MG4_KEYSTORE_PASSWORD }}
MG4_KEY_ALIAS: ${{ secrets.MG4_KEY_ALIAS }}
MG4_KEY_PASSWORD: ${{ secrets.MG4_KEY_PASSWORD }}
run: |
chmod +x gradlew
./gradlew assembleOnlineRelease assembleOfflineRelease --no-daemon \
-Pmg4.versionSuffix='${{ steps.ver.outputs.suffix }}'
- name: Collect APKs
run: |
mkdir -p dist
find app/build/outputs/apk -name "MG4Control-*.apk" -exec cp {} dist/ \;
ls -la dist/
# Controle que l'APK annonce bien la version du tag : c'est la condition pour que l'OTA
# cesse de proposer la mise a jour une fois installee.
- name: Verify APK version matches the tag
run: |
AAPT=$(ls "$ANDROID_SDK_ROOT"/build-tools/*/aapt2 | sort -V | tail -1)
EXPECTED='${{ steps.ver.outputs.full }}'
for f in dist/*.apk; do
GOT=$("$AAPT" dump badging "$f" | grep -oP "versionName='\K[^']+")
echo "$(basename "$f") -> $GOT"
case "$GOT" in
"$EXPECTED"|"$EXPECTED-offline") ;;
*) echo "::error::$(basename "$f") annonce '$GOT', attendu '$EXPECTED'"; exit 1 ;;
esac
done
# /!\ --min-sdk-version 23 est OBLIGATOIRE pour que ce rapport dise la verite. Sans lui,
# apksigner prend le minSdk de l'APK (28) et ne verifie qu'en v3 : il affiche alors
# "Verified using v1 scheme: false" sur un APK qui CONTIENT bien une signature v1.
# Ce faux negatif a deja fait chercher une panne de cle la ou le reseau etait en cause.
- name: Verify signature schemes
run: |
APKSIGNER=$(ls "$ANDROID_SDK_ROOT"/build-tools/*/apksigner | sort -V | tail -1)
for f in dist/*.apk; do
echo "-- $(basename "$f") --"
"$APKSIGNER" verify --print-certs -v --min-sdk-version 23 "$f" \
| grep -E 'Verified using v[0-9]|certificate SHA-256 digest'
# Presence REELLE de la signature v1, independamment de ce qu'apksigner choisit de
# verifier. Elle est indispensable : sur AAOS 9 le controle OTA lit l'archive via
# getPackageArchiveInfo(), qui ne remonte aucun certificat sans v1 - l'application
# refuse alors la mise a jour. Mieux vaut casser le build que publier ca.
if ! unzip -l "$f" | grep -qE 'META-INF/.*[.](SF|RSA|DSA|EC)$'; then
echo "::error::$(basename "$f") n a PAS de signature v1 (JAR) - l OTA la refuserait"
exit 1
fi
# Empreinte du fichier publie. Le vehicule journalise la meme chose apres
# telechargement ("APK telecharge : N octets, sha256=..."). Deux lignes identiques
# prouvent un transfert intact ; une divergence designe le reseau, pas la signature.
echo " v1 (JAR) presente : OUI"
echo " taille : $(stat -c%s "$f") octets"
echo " sha256 : $(sha256sum "$f" | cut -d' ' -f1)"
done
- name: Publish pre-release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ steps.ver.outputs.full }}
# Sans target_commitish, GitHub cree le tag sur la branche par defaut : la beta 2.6.6-beta.2
# a ainsi ete taguee sur main alors qu'elle etait construite depuis beta-OTA. L'APK etait
# bonne, mais le tag pointait vers un code qui n'etait pas celui livre.
target_commitish: ${{ github.sha }}
name: MG4Control ${{ steps.ver.outputs.full }}
prerelease: true
files: dist/*.apk
body: |
## MG4Control ${{ steps.ver.outputs.full }} — test build
> Delivered only to users who enabled
> **Settings -> Advanced settings -> Beta update channel**.
> Rolling back to an earlier version is not possible through the updater.
${{ inputs.notes }}
| Variant | File | Network | Auto-update |
|---|---|---|---|
| **Online** | `MG4Control-online-*.apk` | yes | yes |
| **Offline** | `MG4Control-secure-*-offline.apk` | none | no |
# Purge des anciennes betas. Trois garde-fous, dans cet ordre :
# 1. on ne touche QU'AUX prereleases (select .prerelease==true) — une erreur de filtre
# effacerait les releases stables, d'ou dependent tous les utilisateurs ;
# 2. on ne supprime jamais celle qu'on vient de publier ;
# 3. si elle n'apparait pas encore dans l'API, on n'efface RIEN — mieux vaut une liste
# trop longue qu'une suppression decidee sur un etat incomplet.
#
# On garde les N plus recentes parce que l'OTA ne sait PAS redescendre de version : le seul
# retour arriere depuis une beta cassee est de reinstaller l'APK precedent a la main. Purger
# jusqu'a une seule supprimerait ce filet.
#
# Regle supplementaire : une beta dont la STABLE de meme numero est sortie ne sert plus a
# rien (la stable la remplace pour tout le monde), elle part quel que soit son rang.
- name: Prune old beta releases
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
KEEP_TAG: v${{ steps.ver.outputs.full }}
KEEP_N: ${{ inputs.keep }}
run: |
set -euo pipefail
if ! [ "$KEEP_N" -gt 0 ] 2>/dev/null; then
echo "keep=$KEEP_N : purge desactivee."; exit 0
fi
gh api "repos/$GITHUB_REPOSITORY/releases?per_page=100" > all.json
# Numeros des releases stables publiees (sans le "v" ni le suffixe).
jq -r '.[] | select(.prerelease==false and .draft==false) | .tag_name' all.json \
| sed 's/^v//' | cut -d- -f1 | sort -u > stables.txt
# Prereleases, de la plus recente a la plus ancienne (ordre de l'API).
jq -r '.[] | select(.prerelease==true) | .tag_name' all.json > betas.txt
echo "Stables publiees : $(tr '\n' ' ' < stables.txt)"
echo "Prereleases : $(wc -l < betas.txt)"
if ! grep -qx "$KEEP_TAG" betas.txt; then
echo "::warning::$KEEP_TAG absente de l'API (propagation ?) — purge annulee par securite"
exit 0
fi
kept=0
while read -r tag; do
if [ -z "$tag" ]; then continue; fi
base=$(echo "${tag#v}" | cut -d- -f1)
if [ "$tag" != "$KEEP_TAG" ] && grep -qx "$base" stables.txt; then
echo " supprime $tag — la stable $base est publiee"
gh release delete "$tag" --yes --cleanup-tag
continue
fi
kept=$((kept + 1))
if [ "$kept" -le "$KEEP_N" ] || [ "$tag" = "$KEEP_TAG" ]; then
echo " conserve $tag ($kept/$KEEP_N)"
continue
fi
echo " supprime $tag — au-dela des $KEEP_N conservees"
gh release delete "$tag" --yes --cleanup-tag
done < betas.txt