v0.7.7 - Icone PNG embarquee dans l'en-tete GUI

Changements :
- Icone icon.png embarquee dans l'exe via go:embed
- Affichage de l'icone a gauche du titre dans l'interface
- Mise a jour CLAUDE.md pour refleter la migration Go

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-25 12:42:33 +01:00
parent 18fe9d7186
commit 98b5187bfc
7 changed files with 91 additions and 42 deletions

107
CLAUDE.md
View File

@@ -6,37 +6,53 @@ Outil Windows (.exe) avec interface graphique qui vérifie les mises à jour de
Conçu pour être placé sur une **clé USB** dont la lettre de lecteur peut changer. Conçu pour être placé sur une **clé USB** dont la lettre de lecteur peut changer.
Le programme peut **s'auto-mettre à jour** car il est lui-même dans un dépôt git. Le programme peut **s'auto-mettre à jour** car il est lui-même dans un dépôt git.
## Langage
**Go** (anciennement Python, migré en Go depuis v0.7.x).
- GUI : `github.com/lxn/walk` (contrôles natifs Windows)
- Exe unique, aucune dépendance externe à l'exécution
- Build : `go build` via `build.bat`
## Structure du projet ## Structure du projet
``` ```
Lanceur-geco/ Lanceur-geco/
├── git_updater.py # Script principal Python (GUI tkinter) ├── main.go # Point d'entrée, constante VERSION
├── version.txt # Fichier contenant le numéro de version (ex: "0.5.1") ├── config.go # Chargement config.ini (repos + self-update)
├── config.ini # Configuration multi-repo ├── git.go # Opérations git (check, clone, pull, checkout, clean)
├── build.bat # Script de compilation en .exe via PyInstaller ├── gui.go # Interface graphique (walk/TableView)
├── log/ # Dossier de logs (créé automatiquement, 1 fichier par jour) ├── logger.go # Logging fichier (1 fichier/jour, rotation 30j)
├── selfupdate.go # Auto-mise à jour du programme
├── platform_windows.go # Code spécifique Windows (création processus)
├── version.txt # Numéro de version (utilisé par l'auto-update distant)
├── config.ini # Configuration multi-repo
├── build.bat # Script de compilation en .exe
├── app.manifest # Manifeste Windows (DPI, elevation)
├── icon.ico # Icône application
├── go.mod / go.sum # Dépendances Go
├── log/ # Dossier de logs (créé automatiquement)
└── CLAUDE.md └── CLAUDE.md
``` ```
## Règles importantes ## Règles importantes
- **Tous les chemins doivent être relatifs** à l'emplacement de l'exe. Jamais de chemin absolu (pas de `C:\`, `G:\`, etc.). Utiliser `..` et des chemins relatifs pour référencer les dossiers. - **Tous les chemins doivent être relatifs** à l'emplacement de l'exe. Jamais de chemin absolu (pas de `C:\`, `G:\`, etc.). Utiliser `..` et des chemins relatifs pour référencer les dossiers.
- **Accès lecture seule** : le programme ne fait que `git fetch`, `git pull` et `git checkout`. Jamais de `git push`, `git commit`, `git add`, ou toute opération d'écriture vers le remote. - **Accès lecture seule** : le programme ne fait que `git ls-remote`, `git fetch`, `git pull`, `git checkout` et `git clean`. Jamais de `git push`, `git commit`, `git add`, ou toute opération d'écriture vers le remote.
- **Multi-repo** : le programme peut surveiller plusieurs dépôts Git configurés dans `config.ini`. - **Multi-repo** : le programme peut surveiller plusieurs dépôts Git configurés dans `config.ini`.
## Versioning ## Versioning
- La version est définie en dur dans `git_updater.py` via la constante `VERSION` (ex: `VERSION = "0.5.1"`) - La version est définie en dur dans `main.go` via la constante `VERSION` (ex: `const VERSION = "0.7.6"`)
- Le fichier `version.txt` à la racine du projet contient le même numéro de version (utilisé par le mécanisme d'auto-update distant) - Le fichier `version.txt` à la racine du projet contient le même numéro de version (utilisé par le mécanisme d'auto-update distant)
- Format : **semver simplifié** `MAJEUR.MINEUR.PATCH` (ex: `0.5.1`) - Format : **semver simplifié** `MAJEUR.MINEUR.PATCH` (ex: `0.7.6`)
- **Les deux doivent toujours être synchronisés** : quand on change la version, mettre à jour `VERSION` dans `git_updater.py` ET `version.txt` - **Les deux doivent toujours être synchronisés** : quand on change la version, mettre à jour `VERSION` dans `main.go` ET `version.txt`
### Mise à jour de la version ### Mise à jour de la version
A chaque changement de version, il faut mettre à jour **4 éléments** : A chaque changement de version, il faut mettre à jour **4 éléments** :
1. `VERSION` dans `git_updater.py` (constante en haut du fichier) 1. `VERSION` dans `main.go` (constante en haut du fichier)
2. `version.txt` à la racine du projet 2. `version.txt` à la racine du projet
3. **Recompiler l'exe** via `build.bat` et copier `dist/GitUpdateChecker.exe` à la racine du projet 3. **Recompiler l'exe** via `build.bat` (produit `GitUpdateChecker.exe` à la racine)
4. **Créer un commit** avec le message suivant : 4. **Créer un commit** avec le message suivant :
``` ```
@@ -50,18 +66,18 @@ Changements :
Exemple : Exemple :
``` ```
v0.5.2 - Detection depot hors ligne v0.7.6 - Clone dossier non-vide et verification rapide
Changements : Changements :
- Ajout verification connectivite remote avant fetch (git ls-remote) - Clone dans dossier non-vide (git init + remote add + fetch + checkout)
- Affichage "HORS LIGNE" si le serveur est inaccessible - Verification rapide via git ls-remote au lieu de git fetch
- Synchronisation auto de l'URL origin depuis config.ini - Support branche par repo dans config.ini
``` ```
### Mécanisme de comparaison ### Mécanisme de comparaison
- La fonction `_version_tuple(v)` convertit la chaîne version en tuple d'entiers (ex: `"0.5.1"` -> `(0, 5, 1)`) pour permettre la comparaison numérique - La fonction `parseVersion(v)` convertit la chaîne version en `[3]int` (ex: `"0.7.6"` -> `[0, 7, 6]`) pour permettre la comparaison numérique
- L'auto-update télécharge `version.txt` depuis le serveur Gitea via HTTP (`{repo_url}/raw/branch/master/version.txt`) et compare avec la `VERSION` locale - L'auto-update télécharge `version.txt` depuis le serveur Gitea via HTTP (`{repo_url}/raw/branch/{branch}/version.txt`) et compare avec la `VERSION` locale
- Si la version distante est supérieure, une mise à jour est proposée - Si la version distante est supérieure, une mise à jour est proposée
## Fonctionnement ## Fonctionnement
@@ -71,64 +87,77 @@ Changements :
2. Compare la version distante avec la constante `VERSION` locale (comparaison par tuple numérique) 2. Compare la version distante avec la constante `VERSION` locale (comparaison par tuple numérique)
3. Si la version distante est supérieure, propose de télécharger le nouvel exe 3. Si la version distante est supérieure, propose de télécharger le nouvel exe
4. Stratégie de remplacement : télécharge dans `.new`, renomme l'exe actuel en `.old`, place le nouveau 4. Stratégie de remplacement : télécharge dans `.new`, renomme l'exe actuel en `.old`, place le nouveau
5. Après mise à jour, demande un redémarrage 5. Après mise à jour, lance un script batch de redémarrage
### Vérification des dépôts ### Vérification des dépôts
1. Lit la liste des dépôts depuis `config.ini` (chemins relatifs à l'exe) 1. Lit la liste des dépôts depuis `config.ini` (chemins relatifs à l'exe)
2. Pour chaque dépôt : 2. Pour chaque dépôt (en parallèle via goroutines) :
- `git fetch` pour récupérer l'état distant - `git ls-remote` pour vérifier la disponibilité et comparer les hashs (rapide, timeout 15s)
- Compare commits locaux vs distants - `git status --porcelain` pour détecter les fichiers modifiés/non suivis localement
- Détecte les fichiers supprimés/modifiés localement 3. Affiche le résultat dans une interface graphique (walk/TableView)
3. Affiche le résultat dans une interface graphique (tkinter)
4. Propose pour chaque dépôt : 4. Propose pour chaque dépôt :
- `git pull` si nouveaux commits distants - `git pull` si MAJ disponible (hash distant différent)
- `git checkout -- .` si fichiers locaux manquants/modifiés - `git checkout -- .` si fichiers locaux modifiés
- `git clean -fd` si fichiers non suivis en trop
### Clone dans dossier non-vide
Si le dossier cible existe déjà mais n'a pas de `.git` (ex: repos imbriqués), le programme fait un clone "in-place" :
`git init` + `git remote add` + `git fetch` + `git checkout -b <branch>`
## Configuration (config.ini) ## Configuration (config.ini)
Supporte plusieurs sections `[repo:NomDuRepo]` : Supporte plusieurs sections `[repo:NomDuRepo]` :
```ini ```ini
[repo:Batch] [self-update]
url = http://192.168.1.235:3125/zogzog/Batch url = http://192.168.1.235:3125/zogzog/Lanceur-geco
path = ../Batch exe_name = GitUpdateChecker.exe
branch = master
[repo:Powershell] [repo:Scripts]
url = http://192.168.1.235:3125/zogzog/Powershell url = http://192.168.1.235:3125/zogzog/Scripts
path = ../Powershell path = ../SOFT/Batch/Scripts
[repo:Soft]
url = http://192.168.1.235:3125/zogzog/Soft.git
path = ../SOFT/
branch = master
``` ```
- `url` : URL du dépôt Git distant - `url` : URL du dépôt Git distant
- `path` : Chemin **relatif** vers le dossier local du dépôt (relatif à l'exe) - `path` : Chemin **relatif** vers le dossier local du dépôt (relatif à l'exe)
- `branch` : Branche à suivre (optionnel, défaut: `master`)
## Logging ## Logging
- Les logs sont écrits dans `log/` à côté de l'exe (1 fichier par jour, format `YYYY-MM-DD.log`) - Les logs sont écrits dans `log/` à côté de l'exe (1 fichier par jour, format `YYYY-MM-DD.log`)
- Les vieux logs sont nettoyés automatiquement (30 jours de rétention) - Les vieux logs sont nettoyés automatiquement (30 jours de rétention)
- Chaque action git, erreur, et résultat est loggé avec timestamp - Chaque action git, erreur, et résultat est loggé avec timestamp
- Bouton "Ouvrir les logs" dans la GUI pour accéder au dossier - Bouton "Logs" dans la GUI pour ouvrir le dossier
## Build ## Build
```bat ```bat
build.bat build.bat
``` ```
Requiert Python + pip. Installe PyInstaller automatiquement si absent. Requiert Go installé et dans le PATH. Installe `rsrc` automatiquement si absent.
Produit `dist/GitUpdateChecker.exe`. Copier `config.ini` à côté de l'exe. Produit `GitUpdateChecker.exe` à la racine (exe unique, pas de dépendances).
Flags de build : `-H windowsgui -s -w` (pas de console, symboles strippés).
## Contraintes techniques ## Contraintes techniques
- **Chemins relatifs** : Tout est relatif à l'exe, jamais de chemin absolu - **Chemins relatifs** : Tout est relatif à l'exe, jamais de chemin absolu
- **Encodage** : Force UTF-8 pour les caractères Unicode
- **Clé USB** : Fonctionne sur n'importe quelle lettre de lecteur - **Clé USB** : Fonctionne sur n'importe quelle lettre de lecteur
- **Git requis** : Git doit être installé et dans le PATH de la machine - **Git requis** : Git doit être installé et dans le PATH de la machine
- **Serveur Gitea** : Le remote origin pointe vers une instance Gitea locale (192.168.1.235:3125) - **Serveur Gitea** : Le remote origin pointe vers une instance Gitea locale (192.168.1.235:3125)
- **Lecture seule** : Aucune opération d'écriture vers le remote (pas de push/commit) - **Lecture seule** : Aucune opération d'écriture vers le remote (pas de push/commit)
- **Interface** : GUI tkinter (inclus dans Python, pas de dépendance externe) - **Interface** : GUI native Windows via walk (pas de console)
- **Logs** : Dossier `log/` à côté de l'exe, rotation automatique 30 jours - **Logs** : Dossier `log/` à côté de l'exe, rotation automatique 30 jours
- **Repos imbriqués** : Supporte les dépôts git imbriqués (ex: parent/enfant) via clone in-place
## Conventions ## Conventions
- Langage : Python 3, pas de dépendances externes (seulement stdlib + tkinter) - Langage : Go 1.22+
- Interface : GUI tkinter en français - GUI : github.com/lxn/walk (contrôles natifs Windows)
- Langue : Français pour l'interface utilisateur - Interface en français
- Pas de console (flag `-H windowsgui`)

Binary file not shown.

Binary file not shown.

22
gui.go
View File

@@ -1,7 +1,10 @@
package main package main
import ( import (
"bytes"
_ "embed"
"fmt" "fmt"
"image/png"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@@ -14,6 +17,9 @@ import (
. "github.com/lxn/walk/declarative" . "github.com/lxn/walk/declarative"
) )
//go:embed icon.png
var iconPNG []byte
// ── Modèle TableView ────────────────────────────────────────────────────────── // ── Modèle TableView ──────────────────────────────────────────────────────────
type RepoItem struct { type RepoItem struct {
@@ -206,6 +212,7 @@ func (m *RepoModel) hasUpdates() bool {
type App struct { type App struct {
mw *walk.MainWindow mw *walk.MainWindow
iconView *walk.ImageView
statusLabel *walk.Label statusLabel *walk.Label
tv *walk.TableView tv *walk.TableView
model *RepoModel model *RepoModel
@@ -242,6 +249,12 @@ func (a *App) buildAndRun() error {
Composite{ Composite{
Layout: HBox{MarginsZero: true}, Layout: HBox{MarginsZero: true},
Children: []Widget{ Children: []Widget{
ImageView{
AssignTo: &a.iconView,
MinSize: Size{Width: 24, Height: 24},
MaxSize: Size{Width: 24, Height: 24},
Mode: ImageViewModeIdeal,
},
Label{ Label{
Text: "Git Update Checker v" + VERSION, Text: "Git Update Checker v" + VERSION,
Font: Font{Bold: true, PointSize: 12}, Font: Font{Bold: true, PointSize: 12},
@@ -322,13 +335,20 @@ func (a *App) buildAndRun() error {
return err return err
} }
// Icône fenêtre // Icône fenêtre (depuis fichier .ico externe)
if icoPath := filepath.Join(exeDir(), "icon.ico"); fileExists(icoPath) { if icoPath := filepath.Join(exeDir(), "icon.ico"); fileExists(icoPath) {
if icon, err := walk.NewIconFromFile(icoPath); err == nil { if icon, err := walk.NewIconFromFile(icoPath); err == nil {
a.mw.SetIcon(icon) a.mw.SetIcon(icon)
} }
} }
// Icône dans l'en-tête (depuis PNG embarqué dans l'exe)
if img, err := png.Decode(bytes.NewReader(iconPNG)); err == nil {
if bmp, err := walk.NewBitmapFromImageForDPI(img, 96); err == nil {
a.iconView.SetImage(bmp)
}
}
// Lancer la vérification au démarrage // Lancer la vérification au démarrage
go func() { go func() {
time.Sleep(150 * time.Millisecond) time.Sleep(150 * time.Millisecond)

BIN
icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 793 KiB

View File

@@ -9,7 +9,7 @@ import (
"github.com/lxn/walk" "github.com/lxn/walk"
) )
const VERSION = "0.7.6" const VERSION = "0.7.7"
func exeDir() string { func exeDir() string {
exe, err := os.Executable() exe, err := os.Executable()

View File

@@ -1 +1 @@
0.7.6 0.7.7