diff --git a/cmd/root.go b/cmd/root.go index 31abb85..448ca37 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -8,7 +8,7 @@ import ( var rootCmd = &cobra.Command{ Use: "train", - Short: "Train - легковесный пакетный менеджер", + Short: "Train - сранный пакетный менеджер", } func Execute() { diff --git a/cmd/selfupd.go b/cmd/selfupd.go new file mode 100644 index 0000000..894d61d --- /dev/null +++ b/cmd/selfupd.go @@ -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) +} \ No newline at end of file diff --git a/cmd/version.go b/cmd/version.go new file mode 100644 index 0000000..51dfc4c --- /dev/null +++ b/cmd/version.go @@ -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) +} \ No newline at end of file diff --git a/pkg/autoupdate/autoupdate.go b/pkg/autoupdate/autoupdate.go index 2dbdaed..7178924 100644 --- a/pkg/autoupdate/autoupdate.go +++ b/pkg/autoupdate/autoupdate.go @@ -1,58 +1,131 @@ package autoupdate import ( + "archive/tar" + "compress/gzip" "fmt" + "io" "io/ioutil" "net/http" "os" "os/exec" + "path/filepath" ) -func CheckForUpdates(updateURL string) (bool, string, error) { - resp, err := http.Get(updateURL) +func BuildNewBinary(tarballURL string) (buildDir string, newBinPath string, err error) { + fmt.Printf("Downloading tarball: %s\n", tarballURL) + resp, err := http.Get(tarballURL) if err != nil { - return false, "", err + return "", "", fmt.Errorf("failed to download tarball: %w", err) } defer resp.Body.Close() - data, err := ioutil.ReadAll(resp.Body) + + tmpTarball, err := ioutil.TempFile("", "source_*.tar.gz") if err != nil { - return false, "", err + return "", "", fmt.Errorf("failed to create temp file: %w", err) } - latestVersion := string(data) - currentVersion := "1.0.0" - if latestVersion != currentVersion { - return true, latestVersion, nil + _, err = io.Copy(tmpTarball, resp.Body) + if err != nil { + tmpTarball.Close() + 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 { - available, latest, err := CheckForUpdates(updateURL) +func unpackTarball(archivePath, destDir string) error { + f, err := os.Open(archivePath) if err != nil { return err } - if available { - fmt.Printf("Доступна новая версия: %s. Запускаем обновление...\n", latest) - resp, err := http.Get(updateURL + "/binary") // предположим, что по этому URL лежит бинарник + defer f.Close() + gzr, err := gzip.NewReader(f) + 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 { return err } - defer resp.Body.Close() - data, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err + target := filepath.Join(destDir, header.Name) + switch header.Typeflag { + case tar.TypeDir: + 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 } \ No newline at end of file diff --git a/pkg/version/current.go b/pkg/version/current.go new file mode 100644 index 0000000..cab4018 --- /dev/null +++ b/pkg/version/current.go @@ -0,0 +1,3 @@ +package version + +const CurrentVersion = "1.0.5" \ No newline at end of file