diff --git a/.github/workflows/mac-build.yml b/.github/workflows/mac-build.yml index 390047a..5386ae3 100644 --- a/.github/workflows/mac-build.yml +++ b/.github/workflows/mac-build.yml @@ -17,7 +17,7 @@ jobs: run: | mkdir -p dist CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 \ - go build -trimpath -ldflags="-s -w" -o dist/ajean-darwin-arm64 ./cmd/ajean + go build -trimpath -ldflags="-s -w" -o dist/ajean-macos-arm ./cmd/ajean mkdir -p /tmp/ajean.iconset sips -s format png 'cmd/ajean/icon.ico' --out /tmp/icon.png >/dev/null 2>&1 || true for s in 16 32 128 256 512; do @@ -26,7 +26,7 @@ jobs: iconutil -c icns /tmp/ajean.iconset -o /tmp/ajean.icns >/dev/null 2>&1 || true app="dist/AJEAN.app" mkdir -p "$app/Contents/MacOS" "$app/Contents/Resources" - cp dist/ajean-darwin-arm64 "$app/Contents/MacOS/ajean" + cp dist/ajean-macos-arm "$app/Contents/MacOS/ajean" chmod +x "$app/Contents/MacOS/ajean" cp /tmp/ajean.icns "$app/Contents/Resources/ajean.icns" 2>/dev/null || true printf '%s\n' \ @@ -44,9 +44,9 @@ jobs: 'NSHighResolutionCapable' \ '' > "$app/Contents/Info.plist" codesign --force --deep -s - "$app" || true - (cd dist && zip -qry AJEAN-macos-arm64.zip AJEAN.app && rm -rf AJEAN.app) + (cd dist && zip -qry AJEAN-macos-arm.zip AJEAN.app && rm -rf AJEAN.app) ls -l dist - uses: actions/upload-artifact@v4 with: - name: AJEAN-macos-arm64 - path: dist/AJEAN-macos-arm64.zip + name: AJEAN-macos-arm + path: dist/AJEAN-macos-arm.zip diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5635688..1e76fe5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,11 +29,11 @@ jobs: mkdir -p dist # arm64 = natif ; amd64 = cross avec le SDK universel (-arch x86_64). CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 \ - go build -trimpath -ldflags="-s -w" -o dist/ajean-darwin-arm64 ./cmd/ajean + go build -trimpath -ldflags="-s -w" -o dist/ajean-macos-arm ./cmd/ajean CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 \ CGO_CFLAGS="-arch x86_64" CGO_LDFLAGS="-arch x86_64" \ - go build -trimpath -ldflags="-s -w" -o dist/ajean-darwin-amd64 ./cmd/ajean - file dist/ajean-darwin-* + go build -trimpath -ldflags="-s -w" -o dist/ajean-macos ./cmd/ajean + file dist/ajean-macos* - name: bundles AJEAN.app run: | # Un binaire nu double-cliqué ouvrirait le Terminal. On publie donc @@ -51,10 +51,10 @@ jobs: done iconutil -c icns /tmp/ajean.iconset -o /tmp/ajean.icns v="${GITHUB_REF_NAME#v}" - for arch in amd64 arm64; do - app="dist/mac-$arch/AJEAN.app" + for bin in ajean-macos ajean-macos-arm; do + app="dist/bundle-$bin/AJEAN.app" mkdir -p "$app/Contents/MacOS" "$app/Contents/Resources" - cp "dist/ajean-darwin-$arch" "$app/Contents/MacOS/ajean" + cp "dist/$bin" "$app/Contents/MacOS/ajean" chmod +x "$app/Contents/MacOS/ajean" cp /tmp/ajean.icns "$app/Contents/Resources/ajean.icns" 2>/dev/null || true printf '%s\n' \ @@ -76,12 +76,8 @@ jobs: # Signature ad-hoc : sans elle, macOS refuse purement et simplement # de lancer un binaire arm64 non signé (pas juste un avertissement). codesign --force --deep -s - "$app" || true - (cd "dist/mac-$arch" && zip -qry "../AJEAN-macos-$arch.zip" AJEAN.app) - # Nom hérité conservé : les liens de téléchargement déjà publiés - # (site, mails, messages) pointent dessus et doivent continuer de - # fonctionner. Même contenu, deux noms. - cp "dist/AJEAN-macos-$arch.zip" "dist/AJEAN-macos-$arch.zip" - rm -rf "dist/mac-$arch" + (cd "dist/bundle-$bin" && zip -qry "../AJEAN-${bin#ajean-}.zip" AJEAN.app) + rm -rf "dist/bundle-$bin" done ls -l dist - uses: actions/upload-artifact@v4 @@ -100,13 +96,20 @@ jobs: - name: build Linux + Windows run: | mkdir -p dist - for t in linux/amd64 linux/arm64 windows/amd64 windows/arm64; do - os=${t%/*}; arch=${t#*/}; ext=""; ldflags="-s -w" + # Noms d'asset : ajean-[-arm][.exe]. Volontairement lisibles, et + # volontairement DIFFÉRENTS du schéma ajean-- des 0.7 : + # leur mise à jour automatique ne trouve alors aucun asset et échoue + # sans rien remplacer, au lieu d'installer un binaire 0.8 sur une + # machine encore agencée en 0.7 (voir updateAssetName, sys_update.go). + for t in linux/amd64:ajean-linux linux/arm64:ajean-linux-arm \ + windows/amd64:ajean-windows.exe windows/arm64:ajean-windows-arm.exe; do + target=${t%%:*}; name=${t#*:} + os=${target%/*}; arch=${target#*/}; ldflags="-s -w" # Windows : sous-système GUI → aucune console noire au double-clic # (l'app se rattache à la console du terminal parent si lancée en CLI). - [ "$os" = windows ] && { ext=".exe"; ldflags="$ldflags -H=windowsgui"; } + [ "$os" = windows ] && ldflags="$ldflags -H=windowsgui" GOOS=$os GOARCH=$arch CGO_ENABLED=0 \ - go build -trimpath -ldflags="$ldflags" -o "dist/ajean-$os-$arch$ext" ./cmd/ajean + go build -trimpath -ldflags="$ldflags" -o "dist/$name" ./cmd/ajean done - uses: actions/download-artifact@v4 with: @@ -114,7 +117,7 @@ jobs: path: dist - name: SHA256SUMS run: | - chmod +x dist/ajean-darwin-* + chmod +x dist/ajean-macos* (cd dist && sha256sum ajean-* *.zip > SHA256SUMS) ls -l dist && cat dist/SHA256SUMS - name: publier la release diff --git a/README.md b/README.md index a475509..9513c87 100644 --- a/README.md +++ b/README.md @@ -41,12 +41,12 @@ Aucune dépendance à l'exécution, aucun flag CMake à retenir, aucun conteneur ### 1. Le binaire ```bash -curl -L -o ajean https://github.com/nathaninline/ajean/releases/latest/download/ajean-linux-amd64 +curl -L -o ajean https://github.com/nathaninline/ajean/releases/latest/download/ajean-linux chmod +x ajean sudo mv ajean /usr/local/bin/ajean ``` -(Autres cibles sur la page [Releases](../../releases) : Linux, macOS et Windows, en amd64 et arm64.) +Les binaires publiés : `ajean-linux`, `ajean-linux-arm`, `ajean-macos`, `ajean-macos-arm`, `ajean-windows.exe`, `ajean-windows-arm.exe` — le suffixe `-arm` désigne l'arm64, l'absence de suffixe l'x86-64. ### 2. Installation et compilation du moteur @@ -231,11 +231,11 @@ ajean status ## macOS -La page [Releases](../../releases) publie `AJEAN-macos-arm64.zip` / `AJEAN-macos-amd64.zip`, un bundle **`AJEAN.app`**. Dézipper, glisser dans *Applications*, ouvrir : l'interface démarre sur `http://localhost:8090`, s'ouvre dans le navigateur, et l'icône se pose dans la **barre de menus**. Pas de fenêtre de Terminal, pas d'icône dans le Dock. +La page [Releases](../../releases) publie `AJEAN-macos-arm.zip` (Apple Silicon) et `AJEAN-macos.zip` (Intel), un bundle **`AJEAN.app`**. Dézipper, glisser dans *Applications*, ouvrir : l'interface démarre sur `http://localhost:8090`, s'ouvre dans le navigateur, et l'icône se pose dans la **barre de menus**. Pas de fenêtre de Terminal, pas d'icône dans le Dock. L'application n'est signée qu'en ad-hoc : au premier lancement, faire **clic droit → Ouvrir**. -Pour un usage en ligne de commande, prendre le binaire nu `ajean-darwin-arm64` : hors bundle, il conserve son comportement CLI. Les services passent par **launchd**. +Pour un usage en ligne de commande, prendre le binaire nu `ajean-macos-arm` : hors bundle, il conserve son comportement CLI. Les services passent par **launchd**. ## Accès distant via ajean.link diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 9478d9a..f10f655 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,6 +1,6 @@ Le renommage est terminé. Plus rien ne s'appelle jean : ni le binaire, ni les services, ni les variables, ni les dossiers. Et le dossier de données, qui s'était couvert d'une douzaine de petits fichiers d'état, tient désormais dans six dossiers et une base. -**La reprise d'une installation 0.7 tient en une commande : `sudo ajean install`.** Il déplace les dossiers, reprend les réglages en base, désactive les anciens services et installe les nouveaux. Rien n'est supprimé : presets, mémoire et modèles sont déplacés, les anciens fichiers d'état rangés dans `avant-0.8/`. C'est le seul code de compatibilité de la version, isolé dans un fichier prévu pour être supprimé. +**Une installation 0.7 doit être RÉINSTALLÉE : ne passez pas par le bouton de mise à jour.** Téléchargez le binaire 0.8, puis `sudo ajean install` — il fait la reprise complète. Il déplace les dossiers, reprend les réglages en base, désactive les anciens services et installe les nouveaux. Rien n'est supprimé : presets, mémoire et modèles sont déplacés, les anciens fichiers d'état rangés dans `avant-0.8/`. C'est le seul code de compatibilité de la version, isolé dans un fichier prévu pour être supprimé. ## Une base à la place des fichiers d'état @@ -32,4 +32,6 @@ Tout le code écrit pour ménager les installations « jean » : la migration du La CLI perd ses alias hérités — `skills`, `machine`, `tools`, `web-access`, `mem`, `upgrade`, `self-update`, `paths`, `llama` — et son aide est réorganisée autour des deux services. Chaque commande a désormais un seul nom. `app` quitte l'aide : c'est le comportement du double-clic, qu'on n'atteint pas en tapant son nom. -Les releases ne publient plus qu'un jeu de binaires, `ajean--`. La double publication qui accompagnait la transition n'a plus d'objet. +Les releases ne publient plus qu'un jeu de binaires, et sous des noms lisibles : `ajean-linux`, `ajean-linux-arm`, `ajean-macos`, `ajean-macos-arm`, `ajean-windows.exe`, `ajean-windows-arm.exe`. La double publication qui accompagnait la transition n'a plus d'objet. + +Ce changement de noms n'est pas cosmétique. Les versions 0.7 cherchent leur mise à jour sous la forme `ajean--` : ne trouvant aucun asset qui corresponde, leur bouton « mettre à jour » échoue proprement, sans rien remplacer. C'est délibéré. Laisser la 0.7 installer ce binaire aurait remplacé l'exécutable sans migrer les données ni les unités : le service de lien serait reparti en boucle d'échec sur une sous-commande disparue, et le moteur n'aurait plus trouvé sa configuration — une machine à réparer en SSH après un clic dans un navigateur. diff --git a/internal/ajean/sys_update.go b/internal/ajean/sys_update.go index a9428bf..095c76b 100644 --- a/internal/ajean/sys_update.go +++ b/internal/ajean/sys_update.go @@ -35,13 +35,33 @@ type ghRelease struct { } // updateAssetName est le nom de l'asset de release pour la plateforme courante : -// ajean--[.exe]. Un seul nom publié, un seul nom cherché. -func updateAssetName() string { - suffix := runtime.GOOS + "-" + runtime.GOARCH - if runtime.GOOS == "windows" { - suffix += ".exe" +// ajean-linux, ajean-linux-arm, ajean-macos, ajean-macos-arm, ajean-windows.exe, +// ajean-windows-arm.exe. Un seul nom publié, un seul nom cherché. +// +// Ces noms sont volontairement lisibles — « macos » plutôt que « darwin », pas +// de « amd64 » pour le cas courant — et volontairement DIFFÉRENTS du schéma +// ajean-- qu'utilisaient les versions 0.7 : leur mise à jour +// automatique ne trouve donc aucun asset et échoue sans rien remplacer, au lieu +// d'installer un binaire 0.8 sur une machine encore agencée en 0.7 — ce qui +// laissait le service de lien en boucle d'échec. La 0.7 se met à jour à la main, +// par réinstallation. +func updateAssetName() string { return assetNameFor(runtime.GOOS, runtime.GOARCH) } + +// assetNameFor est séparée pour être vérifiable sur les six plateformes, et pas +// seulement sur celle qui exécute les tests. +func assetNameFor(goos, goarch string) string { + name := "ajean-" + map[string]string{ + "darwin": "macos", + "linux": "linux", + "windows": "windows", + }[goos] + if goarch == "arm64" { + name += "-arm" } - return "ajean-" + suffix + if goos == "windows" { + name += ".exe" + } + return name } // pickAsset trouve dans la release l'asset de cette plateforme. Renvoie son nom diff --git a/internal/ajean/sys_update_asset_test.go b/internal/ajean/sys_update_asset_test.go index 8dbdd59..ce87a7a 100644 --- a/internal/ajean/sys_update_asset_test.go +++ b/internal/ajean/sys_update_asset_test.go @@ -1,6 +1,9 @@ package ajean -import "testing" +import ( + "runtime" + "testing" +) // relWith fabrique une release ne contenant que les assets nommés. func relWith(names ...string) *ghRelease { @@ -34,3 +37,32 @@ func TestPickAssetSansCorrespondance(t *testing.T) { t.Fatalf("URL inattendue: %q", url) } } + +// Le schéma de nommage ne doit PAS coïncider avec ajean-- : c'est +// ce qui empêche la mise à jour automatique d'une 0.7 de s'installer sur une +// machine encore agencée en 0.7. +func TestNomDAssetDistinctDuSchema07(t *testing.T) { + legacy := "ajean-" + runtime.GOOS + "-" + runtime.GOARCH + if runtime.GOOS == "windows" { + legacy += ".exe" + } + if got := updateAssetName(); got == legacy { + t.Fatalf("le nom %q est celui que cherche la 0.7 — sa mise à jour casserait l'installation", got) + } +} + +// Les six noms publiés par la release, vérifiés un par un. +func TestNomsDAssetParPlateforme(t *testing.T) { + for _, c := range []struct{ goos, goarch, want string }{ + {"linux", "amd64", "ajean-linux"}, + {"linux", "arm64", "ajean-linux-arm"}, + {"darwin", "amd64", "ajean-macos"}, + {"darwin", "arm64", "ajean-macos-arm"}, + {"windows", "amd64", "ajean-windows.exe"}, + {"windows", "arm64", "ajean-windows-arm.exe"}, + } { + if got := assetNameFor(c.goos, c.goarch); got != c.want { + t.Errorf("%s/%s → %q, attendu %q", c.goos, c.goarch, got, c.want) + } + } +}