Add make release (pre-release by default) and release signing key support (0.9.2)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Denis Defolie
2026-10-12 17:38:57 +02:00
parent a82fc8b97b
commit 2e636371d9
6 changed files with 141 additions and 5 deletions
+1
View File
@@ -25,6 +25,7 @@ Le `Makefile` est le seul point d'entrée pour les opérations système. Tenir c
- `make screenshot SCENE=… OUT=… ARGS=…` : capture le rendu réel d'une scène dans une fenêtre (outil de développement, ouvre brièvement une fenêtre) - `make screenshot SCENE=… OUT=… ARGS=…` : capture le rendu réel d'une scène dans une fenêtre (outil de développement, ouvre brièvement une fenêtre)
- `make export-android` / `make export-macos` / `make export-web` : APK Android (arm64 + x86_64, aussi pour ChromeOS), application macOS universelle (zip, signature ad hoc), version web - `make export-android` / `make export-macos` / `make export-web` : APK Android (arm64 + x86_64, aussi pour ChromeOS), application macOS universelle (zip, signature ad hoc), version web
- `make serve-web` : exporte puis sert la version web en local (127.0.0.1) et ouvre le navigateur (`tools/serve_web.py`) - `make serve-web` : exporte puis sert la version web en local (127.0.0.1) et ouvre le navigateur (`tools/serve_web.py`)
- `make release` / `make release-dry-run` : publie (ou simule) une Release GitHub « pre-release » depuis `main` à partir de la version de `project.godot` (`tools/release.sh` ; `STABLE=1` pour une version stable). Clé Android de publication hors dépôt, dans `~/.config/aventuriers-des-mots/release.env`
- `make package` : réexporte puis produit les archives distribuables (Linux, Windows, Android, macOS, ChromeOS ; licence + crédits + notice) et `SHA256SUMS` dans `dist/` - `make package` : réexporte puis produit les archives distribuables (Linux, Windows, Android, macOS, ChromeOS ; licence + crédits + notice) et `SHA256SUMS` dans `dist/`
## Conventions de code ## Conventions de code
+14 -5
View File
@@ -8,7 +8,7 @@ GD_SOURCES := autoload scripts tests
.DEFAULT_GOAL := help .DEFAULT_GOAL := help
.PHONY: serve-web help setup import run lint format validate-data test check content-review generate-dictation-audio generate-sounds export-linux export-windows export-android export-macos export-web package screenshot .PHONY: release release-dry-run serve-web help setup import run lint format validate-data test check content-review generate-dictation-audio generate-sounds export-linux export-windows export-android export-macos export-web package screenshot
# Liste les commandes : toute cible suivie de « ## description » y figure. # Liste les commandes : toute cible suivie de « ## description » y figure.
help: ## Affiche cette aide help: ## Affiche cette aide
@@ -71,14 +71,17 @@ package: ## Reexporte tout et produit les archives dans dist/
screenshot: import ## Capture une scene (SCENE=... OUT=... ARGS=...) screenshot: import ## Capture une scene (SCENE=... OUT=... ARGS=...)
$(GODOT_BIN) --path . -s res://tools/screenshot.gd -- $(SCENE) $(OUT) $(ARGS) $(GODOT_BIN) --path . -s res://tools/screenshot.gd -- $(SCENE) $(OUT) $(ARGS)
# Android : APK unique (arm64 + x86_64, donc aussi les Chromebooks). Signature # Android : APK unique (arm64 + x86_64, donc aussi les Chromebooks). La cle de
# de test avec la cle de debogage de Godot (standard, publique) : aucune cle # publication (hors depot) est lue dans RELEASE_ENV : trois variables
# privee dans le depot. Pour publier sur un magasin, fournir sa propre cle via # GODOT_ANDROID_KEYSTORE_RELEASE_PATH / _USER / _PASSWORD. Sans ce fichier, repli
# GODOT_ANDROID_KEYSTORE_RELEASE_PATH / _USER / _PASSWORD. # sur la cle de debogage publique de Godot : acceptable pour un essai local,
# jamais pour une publication (tools/release.sh le refuse).
RELEASE_ENV ?= $(HOME)/.config/aventuriers-des-mots/release.env
ANDROID_DEBUG_KEYSTORE ?= $(HOME)/.local/share/godot/keystores/debug.keystore ANDROID_DEBUG_KEYSTORE ?= $(HOME)/.local/share/godot/keystores/debug.keystore
export-android: import ## Exporte l'APK Android / ChromeOS dans build/android/ export-android: import ## Exporte l'APK Android / ChromeOS dans build/android/
mkdir -p build/android mkdir -p build/android
set -a; [ -f "$(RELEASE_ENV)" ] && . "$(RELEASE_ENV)"; set +a; \
GODOT_ANDROID_KEYSTORE_RELEASE_PATH="$${GODOT_ANDROID_KEYSTORE_RELEASE_PATH:-$(ANDROID_DEBUG_KEYSTORE)}" \ GODOT_ANDROID_KEYSTORE_RELEASE_PATH="$${GODOT_ANDROID_KEYSTORE_RELEASE_PATH:-$(ANDROID_DEBUG_KEYSTORE)}" \
GODOT_ANDROID_KEYSTORE_RELEASE_USER="$${GODOT_ANDROID_KEYSTORE_RELEASE_USER:-androiddebugkey}" \ GODOT_ANDROID_KEYSTORE_RELEASE_USER="$${GODOT_ANDROID_KEYSTORE_RELEASE_USER:-androiddebugkey}" \
GODOT_ANDROID_KEYSTORE_RELEASE_PASSWORD="$${GODOT_ANDROID_KEYSTORE_RELEASE_PASSWORD:-android}" \ GODOT_ANDROID_KEYSTORE_RELEASE_PASSWORD="$${GODOT_ANDROID_KEYSTORE_RELEASE_PASSWORD:-android}" \
@@ -94,3 +97,9 @@ export-web: import ## Exporte la version Web (navigateur, ChromeOS) dans build/w
serve-web: export-web ## Exporte puis sert la version web en local et ouvre le navigateur serve-web: export-web ## Exporte puis sert la version web en local et ouvre le navigateur
$(PYTHON) tools/serve_web.py build/web $(PYTHON) tools/serve_web.py build/web
release: ## Publie une pre-release GitHub (STABLE=1 pour une version stable)
tools/release.sh $(if $(STABLE),--stable,)
release-dry-run: ## Verifie tout et construit les archives sans rien publier
tools/release.sh --dry-run
+1
View File
@@ -29,6 +29,7 @@ make run # runs the game
| `make check` | lint + validate-data + test, must pass before every commit | | `make check` | lint + validate-data + test, must pass before every commit |
| `make content-review` | regenerates `docs/review/content-review.md` (content validation document) | | `make content-review` | regenerates `docs/review/content-review.md` (content validation document) |
| `make generate-dictation-audio` | generates the placeholder dictation audio | | `make generate-dictation-audio` | generates the placeholder dictation audio |
| `make release` / `make release-dry-run` | publishes (or simulates) a GitHub Release |
| `make help` | lists the available commands | | `make help` | lists the available commands |
| `make serve-web` | exports, then serves the web build locally and opens the browser | | `make serve-web` | exports, then serves the web build locally and opens the browser |
| `make export-linux` / `make export-windows` / `make export-android` / `make export-macos` / `make export-web` | exports into `build/` | | `make export-linux` / `make export-windows` / `make export-android` / `make export-macos` / `make export-web` | exports into `build/` |
+1
View File
@@ -29,6 +29,7 @@ make run # lance le jeu
| `make check` | lint + validate-data + test, à passer avant chaque commit | | `make check` | lint + validate-data + test, à passer avant chaque commit |
| `make content-review` | régénère `docs/review/content-review.md` (document de validation du contenu) | | `make content-review` | régénère `docs/review/content-review.md` (document de validation du contenu) |
| `make generate-dictation-audio` | génère l'audio provisoire des dictées | | `make generate-dictation-audio` | génère l'audio provisoire des dictées |
| `make release` / `make release-dry-run` | publie (ou simule) une Release GitHub |
| `make help` | liste les commandes disponibles | | `make help` | liste les commandes disponibles |
| `make serve-web` | exporte puis sert la version web en local et ouvre le navigateur | | `make serve-web` | exporte puis sert la version web en local et ouvre le navigateur |
| `make export-linux` / `make export-windows` / `make export-android` / `make export-macos` / `make export-web` | exports dans `build/` | | `make export-linux` / `make export-windows` / `make export-android` / `make export-macos` / `make export-web` | exports dans `build/` |
+6
View File
@@ -438,3 +438,9 @@ Les corrections et les dictées CE1/CE2 n'ont pas révélé d'autre problème ;
**Son sur la version web (0.9.1)** : Godot 4.6 joue par défaut les sons du web en mode « échantillon » (Web Audio direct), qui ignore les bus créés à l'exécution (Music/Effects) et leurs volumes. Réglage `audio/general/default_playback_type.web = Stream` : le son passe par le moteur audio comme sur bureau. **Non vérifié** : aucun navigateur avec sortie audio dans l'environnement de développement ; à tester à la main (premier clic requis par le navigateur avant tout son). **Son sur la version web (0.9.1)** : Godot 4.6 joue par défaut les sons du web en mode « échantillon » (Web Audio direct), qui ignore les bus créés à l'exécution (Music/Effects) et leurs volumes. Réglage `audio/general/default_playback_type.web = Stream` : le son passe par le moteur audio comme sur bureau. **Non vérifié** : aucun navigateur avec sortie audio dans l'environnement de développement ; à tester à la main (premier clic requis par le navigateur avant tout son).
**Lancement de la version web (0.9.2)** : `make serve-web` (export puis serveur local sur 127.0.0.1, ouverture du navigateur) et, dans l'archive ChromeOS, `lancer-web.sh` / `lancer-web.bat` / `lancer-web.py`. Retour de test manuel de l'utilisateur : Linux, Android et web fonctionnent ; Windows, macOS et ChromeOS restent à tester. README, cahier des charges (public CE1 à CM2, 7–11 ans) et listes de contrôle mis à jour pour CM1/CM2. **Lancement de la version web (0.9.2)** : `make serve-web` (export puis serveur local sur 127.0.0.1, ouverture du navigateur) et, dans l'archive ChromeOS, `lancer-web.sh` / `lancer-web.bat` / `lancer-web.py`. Retour de test manuel de l'utilisateur : Linux, Android et web fonctionnent ; Windows, macOS et ChromeOS restent à tester. README, cahier des charges (public CE1 à CM2, 7–11 ans) et listes de contrôle mis à jour pour CM1/CM2.
**Publication des versions (0.9.2)** — le dépôt est public (AGPL-3.0). Choix validé par l'utilisateur : cible `make release` (choix B), pas de CI pour l'instant.
- **Release** : `tools/release.sh` exige la branche `main` à jour de `origin`, un arbre propre, un tag `vX.Y.Z` inexistant, `make check` vert, puis `make package`, vérifie `SHA256SUMS`, refuse un APK signé avec la clé de débogage Godot, crée le tag annoté et la Release **pre-release** (tant que les enseignants n'ont pas validé le contenu ; `STABLE=1` ensuite). `make release-dry-run` fait tout sauf tag et publication.
- **Clé de publication Android** : générée localement (RSA 4096, validité 10000 jours, alias `aventuriers`), hors dépôt : `~/.local/share/aventuriers-des-mots/release.keystore` ; identifiants dans `~/.config/aventuriers-des-mots/release.env` (droits 600). **À sauvegarder hors de la machine** : une clé perdue ne se remplace pas, et changer de clé oblige les appareils à désinstaller avant de réinstaller. `make export-android` la lit automatiquement ; sans elle, repli sur la clé de débogage (essais locaux seulement).
- **Contrôle des secrets avant ouverture** : historique complet relu (fichiers, motifs de clés/jetons/mots de passe, chemins personnels) : rien. Seule donnée personnelle : l'adresse e-mail de l'auteur dans les métadonnées des commits et dans ce fichier (décision de l'utilisateur).
+118
View File
@@ -0,0 +1,118 @@
#!/usr/bin/env bash
# Publie une Release GitHub a partir de la version de project.godot.
#
# tools/release.sh [--dry-run] [--stable]
#
# Par defaut la Release est marquee « pre-release » (contenu pedagogique en
# brouillon). --dry-run fait toutes les verifications et construit les
# archives, sans tag ni publication. La cle de publication Android est lue
# dans ~/.config/aventuriers-des-mots/release.env (hors depot).
set -euo pipefail
cd "$(dirname "$0")/.."
DRY_RUN=0
PRERELEASE=1
for arg in "$@"; do
case "${arg}" in
--dry-run) DRY_RUN=1 ;;
--stable) PRERELEASE=0 ;;
*) echo "Option inconnue : ${arg}" >&2; exit 2 ;;
esac
done
fail() { echo "ERREUR : $*" >&2; exit 1; }
VERSION="$(sed -n 's/^config\/version="\(.*\)"/\1/p' project.godot)"
[ -n "${VERSION}" ] || fail "config/version absent de project.godot"
TAG="v${VERSION}"
RELEASE_ENV="${RELEASE_ENV:-${HOME}/.config/aventuriers-des-mots/release.env}"
DEBUG_CERT_DN="CN=Godot, OU=Godot Engine"
echo "== Verifications (${TAG}) =="
[ "$(git rev-parse --abbrev-ref HEAD)" = "main" ] || fail "la publication se fait depuis la branche main"
[ -z "$(git status --porcelain)" ] || fail "arbre de travail non propre : commiter d'abord"
git fetch -q origin main
[ "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)" ] || fail "main differe de origin/main : pousser ou tirer d'abord"
if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
fail "le tag ${TAG} existe deja : incrementer config/version dans project.godot"
fi
[ -f "${RELEASE_ENV}" ] || fail "cle de publication absente (${RELEASE_ENV})"
if [ "${DRY_RUN}" -eq 0 ]; then
gh auth status >/dev/null 2>&1 || fail "gh n'est pas authentifie (gh auth login)"
fi
echo "== make check =="
make check
echo "== make package =="
make package
[ -z "$(git status --porcelain)" ] || fail "make package a modifie le depot (versions des presets ?) : verifier puis commiter"
echo "== Controle des archives =="
ARCHIVES=(
"dist/aventuriers-des-mots-${VERSION}-linux-x86_64.tar.gz"
"dist/aventuriers-des-mots-${VERSION}-windows-x86_64.zip"
"dist/aventuriers-des-mots-${VERSION}-android.zip"
"dist/aventuriers-des-mots-${VERSION}-macos-universal.zip"
"dist/aventuriers-des-mots-${VERSION}-chromeos.zip"
)
for archive in "${ARCHIVES[@]}"; do
[ -s "${archive}" ] || fail "archive manquante : ${archive}"
done
(cd dist && sha256sum -c SHA256SUMS >/dev/null) || fail "SHA256SUMS ne correspond pas aux archives"
# L'APK ne doit jamais etre signe avec la cle de debogage publique de Godot.
APKSIGNER="$(ls -d "${ANDROID_HOME:-${HOME}/Android/Sdk}"/build-tools/*/apksigner 2>/dev/null | sort -V | tail -1)"
[ -x "${APKSIGNER}" ] || fail "apksigner introuvable (Android SDK build-tools)"
TMP="$(mktemp -d)"
trap 'rm -rf "${TMP}"' EXIT
unzip -q -j "dist/aventuriers-des-mots-${VERSION}-android.zip" '*.apk' -d "${TMP}"
CERTS="$("${APKSIGNER}" verify --print-certs "${TMP}"/*.apk)"
if grep -q "${DEBUG_CERT_DN}" <<<"${CERTS}"; then
fail "l'APK est signe avec la cle de debogage Godot : verifier ${RELEASE_ENV}"
fi
echo "${CERTS}" | sed -n '1,2p'
NOTES="${TMP}/notes.md"
cat > "${NOTES}" <<NOTES
Les Aventuriers des mots ${VERSION}
**Version préliminaire.** Le contenu pédagogique est entièrement en brouillon
(\`review_status: draft\`) et en attente de validation par des enseignants ;
les voix des dictées sont provisoires (synthèse vocale).
| Plateforme | Archive | Testé à la main |
|---|---|---|
| Linux | \`linux-x86_64.tar.gz\` | oui |
| Android | \`android.zip\` (APK) | oui |
| Navigateur / ChromeOS | \`chromeos.zip\` (version web + APK) | web : oui |
| Windows | \`windows-x86_64.zip\` | non |
| macOS | \`macos-universal.zip\` | non |
Chaque archive contient un fichier LISEZMOI, la licence (AGPL-3.0) et les
crédits. Vérifier l'intégrité avec \`SHA256SUMS\`. Les exécutables ne sont pas
signés par un éditeur identifié : Windows SmartScreen et macOS Gatekeeper
demandent une confirmation au premier lancement.
L'APK est signé avec la clé de publication du projet : une mise à jour par
dessus une installation existante fonctionne.
NOTES
if [ "${DRY_RUN}" -eq 1 ]; then
echo "== Simulation terminee : rien n'a ete tague ni publie =="
echo "Commandes qui seraient lancees :"
echo " git tag -a ${TAG} -m 'Release ${VERSION}' && git push origin ${TAG}"
echo " gh release create ${TAG} dist/* $([ "${PRERELEASE}" -eq 1 ] && echo --prerelease)"
exit 0
fi
echo "== Publication =="
git tag -a "${TAG}" -m "Release ${VERSION}"
git push origin "${TAG}"
gh release create "${TAG}" \
"${ARCHIVES[@]}" dist/SHA256SUMS \
--title "Les Aventuriers des mots ${VERSION}" \
--notes-file "${NOTES}" \
$([ "${PRERELEASE}" -eq 1 ] && echo --prerelease)
echo "Publie : $(gh release view "${TAG}" --json url -q .url)"