Repository navigation
218 lines (197 loc) · 9.97 KB
/
Copy pathbeta.yml
File metadata and controls
218 lines (197 loc) · 9.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
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