feature/go-rewrite : base Go avec walk GUI

- Rewrite complet en Go : exe unique sans _internal/, sans extraction temp
- GUI Windows-native via github.com/lxn/walk (TableView, TextEdit, PushButton)
- Meme fonctionnalites : check repos, pull, checkout, auto-update, logs
- build.bat : go build -ldflags "-H windowsgui -s -w" -> 9.6 Mo, zero dependance

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-25 07:33:26 +01:00
parent 959298fc2d
commit 50c8ad9823
964 changed files with 991 additions and 116191 deletions

134
git.go Normal file
View File

@@ -0,0 +1,134 @@
package main
import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
)
type RepoResult struct {
Name string
Path string
URL string
Pending bool
UpToDate bool
Offline bool
NeedsClone bool
Error string
NewCommits int
LocalChanges int
}
func runGit(args []string, cwd string, timeout time.Duration) (code int, stdout string, stderr string) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
fullArgs := append([]string{"-c", "safe.directory=*"}, args...)
cmd := newGitCmd(ctx, fullArgs, cwd)
var outBuf, errBuf strings.Builder
cmd.Stdout = &outBuf
cmd.Stderr = &errBuf
err := cmd.Run()
stdout = strings.TrimSpace(outBuf.String())
stderr = strings.TrimSpace(errBuf.String())
if err != nil {
if ctx.Err() == context.DeadlineExceeded {
return 1, stdout, "Timeout"
}
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
return exitErr.ExitCode(), stdout, stderr
}
return -1, stdout, err.Error()
}
return 0, stdout, stderr
}
func absRepoPath(rel string) string {
if filepath.IsAbs(rel) {
return rel
}
return filepath.Join(exeDir(), rel)
}
func checkRepo(cfg RepoConfig) RepoResult {
res := RepoResult{Name: cfg.Name, URL: cfg.URL}
local := absRepoPath(cfg.Path)
res.Path = local
if _, err := os.Stat(filepath.Join(local, ".git")); os.IsNotExist(err) {
res.NeedsClone = true
return res
}
runGit([]string{"remote", "set-url", "origin", cfg.URL}, local, 10*time.Second)
code, _, stderr := runGit([]string{"fetch", "origin"}, local, 60*time.Second)
if code != 0 {
for _, kw := range []string{"could not resolve", "connection refused", "unable to connect", "timed out", "the remote end hung up"} {
if strings.Contains(strings.ToLower(stderr), kw) {
res.Offline = true
res.Error = "Hors ligne"
return res
}
}
res.Error = "Fetch: " + stderr
return res
}
_, branch, _ := runGit([]string{"rev-parse", "--abbrev-ref", "HEAD"}, local, 5*time.Second)
if branch == "" {
branch = "master"
}
_, countStr, _ := runGit([]string{"rev-list", "--count", "HEAD..origin/" + branch}, local, 5*time.Second)
fmt.Sscanf(countStr, "%d", &res.NewCommits)
_, status, _ := runGit([]string{"status", "--porcelain"}, local, 5*time.Second)
if status != "" {
res.LocalChanges = len(strings.Split(strings.TrimSpace(status), "\n"))
}
res.UpToDate = res.NewCommits == 0 && res.LocalChanges == 0
return res
}
func doClone(cfg RepoConfig) error {
local := absRepoPath(cfg.Path)
if err := os.MkdirAll(filepath.Dir(local), 0755); err != nil {
return err
}
code, _, stderr := runGit([]string{"clone", cfg.URL, local}, "", 300*time.Second)
if code != 0 {
return fmt.Errorf("%s", stderr)
}
return nil
}
func doPull(res RepoResult) error {
_, branch, _ := runGit([]string{"rev-parse", "--abbrev-ref", "HEAD"}, res.Path, 5*time.Second)
if branch == "" {
branch = "master"
}
code, _, stderr := runGit([]string{"pull", "origin", branch}, res.Path, 120*time.Second)
if code != 0 {
return fmt.Errorf("%s", stderr)
}
return nil
}
func doCheckout(res RepoResult) error {
code, _, stderr := runGit([]string{"checkout", "--", "."}, res.Path, 30*time.Second)
if code != 0 {
return fmt.Errorf("%s", stderr)
}
return nil
}