This commit is contained in:
wheelchairy 2025-02-04 19:38:37 +03:00
parent ca822b5f38
commit 72211036c1
5 changed files with 190 additions and 31 deletions

View File

@ -8,7 +8,7 @@ import (
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{
Use: "train", Use: "train",
Short: "Train - легковесный пакетный менеджер", Short: "Train - сранный пакетный менеджер",
} }
func Execute() { func Execute() {

63
cmd/selfupd.go Normal file
View File

@ -0,0 +1,63 @@
package cmd
import (
"fmt"
"io"
"os"
"train/pkg/autoupdate"
"github.com/spf13/cobra"
)
var selfupdCmd = &cobra.Command{
Use: "selfupd",
Short: "Обновляет текущий бинарник Train из исходников",
Run: func(cmd *cobra.Command, args []string) {
tarballURL := "http://212.113.119.5/lain/train-osource/archive/main.tar.gz"
fmt.Println("Обновление Train из исходников...")
tmpDir, newBinPath, err := autoupdate.BuildNewBinary(tarballURL)
if err != nil {
fmt.Printf("Ошибка сборки: %v\n", err)
os.Exit(1)
}
currentPath, err := os.Executable()
if err != nil {
fmt.Printf("Ошибка получения пути текущего бинарника: %v\n", err)
os.Exit(1)
}
fmt.Printf("Замена текущего бинарника (%s) на новый\n", currentPath)
if err := replaceBinary(newBinPath, currentPath); err != nil {
fmt.Printf("Ошибка обновления бинарника: %v\n", err)
os.Exit(1)
}
os.RemoveAll(tmpDir)
fmt.Println("Обновление успешно. Перезапустите Train.")
os.Exit(0)
},
}
func replaceBinary(newPath, currentPath string) error {
src, err := os.Open(newPath)
if err != nil {
return err
}
defer src.Close()
tmpPath := currentPath + ".new"
dst, err := os.Create(tmpPath)
if err != nil {
return err
}
defer dst.Close()
if _, err = io.Copy(dst, src); err != nil {
return err
}
if err = os.Chmod(tmpPath, 0755); err != nil {
return err
}
return os.Rename(tmpPath, currentPath)
}
func init() {
rootCmd.AddCommand(selfupdCmd)
}

20
cmd/version.go Normal file
View File

@ -0,0 +1,20 @@
package cmd
import (
"fmt"
"train/pkg/version"
"github.com/spf13/cobra"
)
var versionCmd = &cobra.Command{
Use: "version",
Short: "Выводит текущую версию Train",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Train version %s\n", version.CurrentVersion)
},
}
func init() {
rootCmd.AddCommand(versionCmd)
}

View File

@ -1,58 +1,131 @@
package autoupdate package autoupdate
import ( import (
"archive/tar"
"compress/gzip"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
) )
func CheckForUpdates(updateURL string) (bool, string, error) { func BuildNewBinary(tarballURL string) (buildDir string, newBinPath string, err error) {
resp, err := http.Get(updateURL) fmt.Printf("Downloading tarball: %s\n", tarballURL)
resp, err := http.Get(tarballURL)
if err != nil { if err != nil {
return false, "", err return "", "", fmt.Errorf("failed to download tarball: %w", err)
} }
defer resp.Body.Close() defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
tmpTarball, err := ioutil.TempFile("", "source_*.tar.gz")
if err != nil { if err != nil {
return false, "", err return "", "", fmt.Errorf("failed to create temp file: %w", err)
} }
latestVersion := string(data) _, err = io.Copy(tmpTarball, resp.Body)
currentVersion := "1.0.0" if err != nil {
if latestVersion != currentVersion { tmpTarball.Close()
return true, latestVersion, nil return "", "", fmt.Errorf("failed to write tarball: %w", err)
} }
return false, currentVersion, nil tmpTarball.Close()
defer os.Remove(tmpTarball.Name())
tmpDir, err := ioutil.TempDir("", "build_")
if err != nil {
return "", "", fmt.Errorf("failed to create temp directory: %w", err)
}
fmt.Printf("Unpacking tarball to: %s\n", tmpDir)
if err := unpackTarball(tmpTarball.Name(), tmpDir); err != nil {
os.RemoveAll(tmpDir)
return "", "", fmt.Errorf("failed to unpack tarball: %w", err)
}
// Пытаемся найти go.mod в корне распакованного архива
goModPath := filepath.Join(tmpDir, "go.mod")
if _, err := os.Stat(goModPath); os.IsNotExist(err) {
// Если не найден, проверяем, есть ли ровно один подкаталог
entries, err := ioutil.ReadDir(tmpDir)
if err != nil {
os.RemoveAll(tmpDir)
return "", "", fmt.Errorf("failed to list unpacked directory: %w", err)
}
var subDir string
count := 0
for _, entry := range entries {
if entry.IsDir() {
subDir = filepath.Join(tmpDir, entry.Name())
count++
}
}
if count == 1 {
goModPath = filepath.Join(subDir, "go.mod")
if _, err := os.Stat(goModPath); err != nil {
os.RemoveAll(tmpDir)
return "", "", fmt.Errorf("go.mod not found in subdirectory")
}
buildDir = subDir
} else {
os.RemoveAll(tmpDir)
return "", "", fmt.Errorf("go.mod not found in unpacked directory")
}
} else {
buildDir = tmpDir
}
newBinPath = filepath.Join(buildDir, "train")
fmt.Println("go.mod found, building using go build...")
cmd := exec.Command("go", "build", "-o", "train")
cmd.Dir = buildDir
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
os.RemoveAll(tmpDir)
return "", "", fmt.Errorf("go build failed: %w", err)
}
fmt.Println("Build completed successfully using go build")
return buildDir, newBinPath, nil
} }
func AutoUpdate(updateURL string) error { func unpackTarball(archivePath, destDir string) error {
available, latest, err := CheckForUpdates(updateURL) f, err := os.Open(archivePath)
if err != nil { if err != nil {
return err return err
} }
if available { defer f.Close()
fmt.Printf("Доступна новая версия: %s. Запускаем обновление...\n", latest) gzr, err := gzip.NewReader(f)
resp, err := http.Get(updateURL + "/binary") // предположим, что по этому URL лежит бинарник if err != nil {
return err
}
defer gzr.Close()
tr := tar.NewReader(gzr)
for {
header, err := tr.Next()
if err == io.EOF {
break
}
if err != nil { if err != nil {
return err return err
} }
defer resp.Body.Close() target := filepath.Join(destDir, header.Name)
data, err := ioutil.ReadAll(resp.Body) switch header.Typeflag {
if err != nil { case tar.TypeDir:
return err if err := os.MkdirAll(target, os.FileMode(header.Mode)); err != nil {
return err
}
case tar.TypeReg:
outFile, err := os.Create(target)
if err != nil {
return err
}
if _, err := io.Copy(outFile, tr); err != nil {
outFile.Close()
return err
}
outFile.Close()
} }
tmpFile := "/tmp/train_new"
if err := ioutil.WriteFile(tmpFile, data, 0755); err != nil {
return err
}
cmd := exec.Command(tmpFile)
if err := cmd.Start(); err != nil {
return err
}
os.Exit(0)
} else {
fmt.Println("Обновлений не найдено.")
} }
return nil return nil
} }

3
pkg/version/current.go Normal file
View File

@ -0,0 +1,3 @@
package version
const CurrentVersion = "1.0.5"