Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c12782dfc9 | ||
![]() |
72cd03aded | ||
![]() |
2640d53ec9 | ||
![]() |
92880855ea | ||
![]() |
da4c6edf2a | ||
![]() |
8d5d845376 | ||
![]() |
7f632f4188 | ||
![]() |
bd5c28e76a | ||
![]() |
bf5a670f80 | ||
![]() |
b9fe96e56c | ||
![]() |
00a56fa985 | ||
![]() |
a606043450 | ||
![]() |
9b4e21691c | ||
![]() |
aaab254cc9 | ||
d401a16b3d | |||
85c79827db | |||
15a1a7f8f0 | |||
c504b6c7cc | |||
![]() |
3d9a1e38df | ||
![]() |
72211036c1 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@ installed*
|
|||||||
repo
|
repo
|
||||||
train
|
train
|
||||||
pkg/*/*.save
|
pkg/*/*.save
|
||||||
|
train-pkg*
|
||||||
|
31
README.md
Normal file
31
README.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Train
|
||||||
|
|
||||||
|
#### РЖД - Российские Железные Дороги
|
||||||
|
|
||||||
|
Сборка:
|
||||||
|
```
|
||||||
|
go mod tidy
|
||||||
|
go build -o train
|
||||||
|
```
|
||||||
|
|
||||||
|
Из бинарника в команды:
|
||||||
|
```
|
||||||
|
./train install-all
|
||||||
|
```
|
||||||
|
|
||||||
|
Дальше пропишите у себя в .*rc:
|
||||||
|
```
|
||||||
|
export PATH="$HOME/.rails/bin:$PATH"
|
||||||
|
export PATH="$HOME/.rails/installed_bins:$PATH"
|
||||||
|
```
|
||||||
|
|
||||||
|
И будет вам счастье
|
||||||
|
|
||||||
|
## Пока-что есть два пакета
|
||||||
|
* choochoo-build
|
||||||
|
* fastfetch/neofetch (later)
|
||||||
|
|
||||||
|
## Обновлятся вот так
|
||||||
|
```
|
||||||
|
train selfupd
|
||||||
|
```
|
11
cmd/root.go
11
cmd/root.go
@ -1,14 +1,21 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
Use: "train",
|
Use: "train",
|
||||||
Short: "Train - легковесный пакетный менеджер",
|
Short: "Train - Лёгкий пакетный менеджер",
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
fmt.Println("[WARNING] Train работает на Windows в режиме совместимости. Гарантий работы нет.")
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func Execute() {
|
func Execute() {
|
||||||
|
63
cmd/selfupd.go
Normal file
63
cmd/selfupd.go
Normal 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 := "https://git.iwakurahome.ru/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
20
cmd/version.go
Normal 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)
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"repositories": [
|
"repo": "https://xn--80abmlgju2eo3byb.xn--p1ai/repo/repo.json",
|
||||||
"https://xn--80abmlgju2eo3byb.xn--p1ai/sklad"
|
"repositories": [],
|
||||||
]
|
"packages": {}
|
||||||
}
|
}
|
||||||
|
@ -1,58 +1,129 @@
|
|||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AutoUpdate(updateURL string) error {
|
fmt.Printf("Unpacking tarball to: %s\n", tmpDir)
|
||||||
available, latest, err := CheckForUpdates(updateURL)
|
if err := unpackTarball(tmpTarball.Name(), tmpDir); err != nil {
|
||||||
|
os.RemoveAll(tmpDir)
|
||||||
|
return "", "", fmt.Errorf("failed to unpack tarball: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
goModPath := filepath.Join(tmpDir, "go.mod")
|
||||||
|
if _, err := os.Stat(goModPath); os.IsNotExist(err) {
|
||||||
|
entries, err := ioutil.ReadDir(tmpDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
os.RemoveAll(tmpDir)
|
||||||
|
return "", "", fmt.Errorf("failed to list unpacked directory: %w", err)
|
||||||
}
|
}
|
||||||
if available {
|
var subDir string
|
||||||
fmt.Printf("Доступна новая версия: %s. Запускаем обновление...\n", latest)
|
count := 0
|
||||||
resp, err := http.Get(updateURL + "/binary") // предположим, что по этому URL лежит бинарник
|
for _, entry := range entries {
|
||||||
if err != nil {
|
if entry.IsDir() {
|
||||||
return err
|
subDir = filepath.Join(tmpDir, entry.Name())
|
||||||
|
count++
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
|
||||||
data, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
tmpFile := "/tmp/train_new"
|
if count == 1 {
|
||||||
if err := ioutil.WriteFile(tmpFile, data, 0755); err != nil {
|
goModPath = filepath.Join(subDir, "go.mod")
|
||||||
return err
|
if _, err := os.Stat(goModPath); err != nil {
|
||||||
|
os.RemoveAll(tmpDir)
|
||||||
|
return "", "", fmt.Errorf("go.mod not found in subdirectory")
|
||||||
}
|
}
|
||||||
cmd := exec.Command(tmpFile)
|
buildDir = subDir
|
||||||
if err := cmd.Start(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
os.Exit(0)
|
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Обновлений не найдено.")
|
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 unpackTarball(archivePath, destDir string) error {
|
||||||
|
f, err := os.Open(archivePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
@ -2,33 +2,35 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"train/pkg/paths"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Repo string `json:"repo"`
|
||||||
|
Repositories []string `json:"repositories"`
|
||||||
|
Packages map[string]string `json:"packages"`
|
||||||
|
}
|
||||||
|
|
||||||
var configFile string
|
var configFile string
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
railsConfig := filepath.Join(paths.BaseDir(), "config", "config.json")
|
home, err := os.UserHomeDir()
|
||||||
|
if err == nil {
|
||||||
|
railsConfig := filepath.Join(home, ".rails", "config", "config.json")
|
||||||
if _, err := os.Stat(railsConfig); err == nil {
|
if _, err := os.Stat(railsConfig); err == nil {
|
||||||
configFile = railsConfig
|
configFile = railsConfig
|
||||||
} else {
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
configFile = "./config.json"
|
configFile = "./config.json"
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
Repositories []string `json:"repositories"`
|
|
||||||
Packages map[string]string `json:"packages"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadConfig() (*Config, error) {
|
func LoadConfig() (*Config, error) {
|
||||||
if _, err := os.Stat(configFile); os.IsNotExist(err) {
|
if _, err := os.Stat(configFile); os.IsNotExist(err) {
|
||||||
cfg := &Config{
|
cfg := &Config{
|
||||||
|
Repo: "",
|
||||||
Repositories: []string{},
|
Repositories: []string{},
|
||||||
Packages: make(map[string]string),
|
Packages: make(map[string]string),
|
||||||
}
|
}
|
||||||
@ -63,7 +65,7 @@ func AddRepository(repo string) error {
|
|||||||
}
|
}
|
||||||
for _, r := range cfg.Repositories {
|
for _, r := range cfg.Repositories {
|
||||||
if r == repo {
|
if r == repo {
|
||||||
return errors.New("репозиторий уже добавлен")
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cfg.Repositories = append(cfg.Repositories, repo)
|
cfg.Repositories = append(cfg.Repositories, repo)
|
||||||
@ -76,16 +78,10 @@ func RemoveRepository(repo string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
newRepos := []string{}
|
newRepos := []string{}
|
||||||
found := false
|
|
||||||
for _, r := range cfg.Repositories {
|
for _, r := range cfg.Repositories {
|
||||||
if r == repo {
|
if r != repo {
|
||||||
found = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
newRepos = append(newRepos, r)
|
newRepos = append(newRepos, r)
|
||||||
}
|
}
|
||||||
if !found {
|
|
||||||
return errors.New("репозиторий не найден")
|
|
||||||
}
|
}
|
||||||
cfg.Repositories = newRepos
|
cfg.Repositories = newRepos
|
||||||
return SaveConfig(cfg)
|
return SaveConfig(cfg)
|
||||||
|
@ -26,8 +26,14 @@ var (
|
|||||||
buildInstallDir = filepath.Join(paths.BaseDir(), "installed_packages")
|
buildInstallDir = filepath.Join(paths.BaseDir(), "installed_packages")
|
||||||
)
|
)
|
||||||
|
|
||||||
func logMessage(msg string) {
|
func logInfo(format string, args ...interface{}) {
|
||||||
fmt.Println(msg)
|
msg := fmt.Sprintf(format, args...)
|
||||||
|
fmt.Printf("\033[32m==>\033[0m %s\n", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func logTrain(format string, args ...interface{}) {
|
||||||
|
msg := fmt.Sprintf(format, args...)
|
||||||
|
fmt.Printf("\U0001F682 %s\n", msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func computeChecksum(filePath string) (string, error) {
|
func computeChecksum(filePath string) (string, error) {
|
||||||
@ -40,7 +46,7 @@ func computeChecksum(filePath string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func downloadFile(url string) (string, error) {
|
func downloadFile(url string) (string, error) {
|
||||||
logMessage("Downloading " + url + "...")
|
logInfo("Downloading %s", url)
|
||||||
resp, err := http.Get(url)
|
resp, err := http.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -52,16 +58,17 @@ func downloadFile(url string) (string, error) {
|
|||||||
}
|
}
|
||||||
_, err = io.Copy(tmpFile, resp.Body)
|
_, err = io.Copy(tmpFile, resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
tmpFile.Close()
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
tmpFile.Close()
|
tmpFile.Close()
|
||||||
logMessage("File downloaded to temporary location")
|
logInfo("File downloaded to temporary location")
|
||||||
return tmpFile.Name(), nil
|
return tmpFile.Name(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func unpackPackage(filePath, packageName string) error {
|
func unpackPackage(filePath, packageName string) error {
|
||||||
|
logInfo("Unpacking package into %s", filepath.Join(buildInstallDir, packageName))
|
||||||
destDir := filepath.Join(buildInstallDir, packageName)
|
destDir := filepath.Join(buildInstallDir, packageName)
|
||||||
logMessage("Unpacking package into " + destDir)
|
|
||||||
if err := os.MkdirAll(destDir, os.ModePerm); err != nil {
|
if err := os.MkdirAll(destDir, os.ModePerm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -102,28 +109,27 @@ func unpackPackage(filePath, packageName string) error {
|
|||||||
outFile.Close()
|
outFile.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logMessage("Unpacking completed")
|
logInfo("Unpacking completed")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func executeBuildCommands(commands []string, packageName string) error {
|
func executeBuildCommands(commands []string, packageName string) error {
|
||||||
packagePath := filepath.Join(buildInstallDir, packageName)
|
packagePath := filepath.Join(buildInstallDir, packageName)
|
||||||
for _, commandStr := range commands {
|
for _, commandStr := range commands {
|
||||||
logMessage("Executing build command: " + commandStr)
|
logInfo("Executing build command: %s", commandStr)
|
||||||
cmd := exec.Command("sh", "-c", commandStr)
|
cmd := exec.Command("sh", "-c", commandStr)
|
||||||
cmd.Dir = packagePath
|
cmd.Dir = packagePath
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("command '%s' failed: %s", commandStr, string(output))
|
return fmt.Errorf("command '%s' failed: %s", commandStr, string(output))
|
||||||
}
|
}
|
||||||
logMessage("Command output: " + string(output))
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveInstalledPackage(name string, m *manifest.Manifest) error {
|
func saveInstalledPackage(name string, m *manifest.Manifest) error {
|
||||||
|
logInfo("Saving manifest to %s", filepath.Join(buildInstallDir, name))
|
||||||
packagePath := filepath.Join(buildInstallDir, name)
|
packagePath := filepath.Join(buildInstallDir, name)
|
||||||
logMessage("Saving manifest to " + packagePath)
|
|
||||||
if err := os.MkdirAll(packagePath, os.ModePerm); err != nil {
|
if err := os.MkdirAll(packagePath, os.ModePerm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -135,29 +141,29 @@ func saveInstalledPackage(name string, m *manifest.Manifest) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func InstallPackage(name string) error {
|
func InstallPackage(name string) error {
|
||||||
logMessage("Fetching manifest for package " + name)
|
logTrain("Installing package: %s", name)
|
||||||
m, err := manifest.FetchManifest(name)
|
m, err := manifest.FetchManifest(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to fetch manifest: %w", err)
|
return fmt.Errorf("failed to fetch manifest: %w", err)
|
||||||
}
|
}
|
||||||
if strings.ToLower(m.Mode) == "bin" {
|
if strings.ToLower(m.Mode) == "bin" {
|
||||||
logMessage("Installing in binary mode")
|
logInfo("Installing in binary mode")
|
||||||
platformKey := runtime.GOOS + "_" + runtime.GOARCH
|
platformKey := runtime.GOOS + "_" + runtime.GOARCH
|
||||||
binaryURL, ok := m.Binaries[platformKey]
|
binaryInfo, ok := m.Binaries[platformKey]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("no binary available for platform %s", platformKey)
|
return fmt.Errorf("no binary available for platform %s", platformKey)
|
||||||
}
|
}
|
||||||
packageFile, err := downloadFile(binaryURL)
|
packageFile, err := downloadFile(binaryInfo.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to download binary: %w", err)
|
return fmt.Errorf("failed to download binary: %w", err)
|
||||||
}
|
}
|
||||||
defer os.Remove(packageFile)
|
defer os.Remove(packageFile)
|
||||||
checksum, err := computeChecksum(packageFile)
|
computedChecksum, err := computeChecksum(packageFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to compute checksum: %w", err)
|
return fmt.Errorf("failed to compute checksum: %w", err)
|
||||||
}
|
}
|
||||||
if checksum != m.Checksum {
|
if computedChecksum != binaryInfo.Checksum {
|
||||||
return fmt.Errorf("checksum mismatch: expected %s, got %s", m.Checksum, checksum)
|
return fmt.Errorf("checksum mismatch: expected %s, got %s", binaryInfo.Checksum, computedChecksum)
|
||||||
}
|
}
|
||||||
if err := os.MkdirAll(binInstallDir, os.ModePerm); err != nil {
|
if err := os.MkdirAll(binInstallDir, os.ModePerm); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -170,21 +176,22 @@ func InstallPackage(name string) error {
|
|||||||
if err := ioutil.WriteFile(destPath, data, 0755); err != nil {
|
if err := ioutil.WriteFile(destPath, data, 0755); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logMessage("Binary package " + m.Name + " installed to " + destPath)
|
logInfo("Binary package %s installed in %s", m.Name, destPath)
|
||||||
|
logTrain("Package %s successfully installed.", m.Name)
|
||||||
return nil
|
return nil
|
||||||
} else if strings.ToLower(m.Mode) == "build" {
|
} else if strings.ToLower(m.Mode) == "build" {
|
||||||
logMessage("Installing in build mode")
|
logInfo("Installing in build mode")
|
||||||
packageFile, err := downloadFile(m.Source)
|
packageFile, err := downloadFile(m.Source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to download package: %w", err)
|
return fmt.Errorf("failed to download package: %w", err)
|
||||||
}
|
}
|
||||||
defer os.Remove(packageFile)
|
defer os.Remove(packageFile)
|
||||||
checksum, err := computeChecksum(packageFile)
|
computedChecksum, err := computeChecksum(packageFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to compute checksum: %w", err)
|
return fmt.Errorf("failed to compute checksum: %w", err)
|
||||||
}
|
}
|
||||||
if checksum != m.Checksum {
|
if computedChecksum != m.Checksum {
|
||||||
return fmt.Errorf("checksum mismatch: expected %s, got %s", m.Checksum, checksum)
|
return fmt.Errorf("checksum mismatch: expected %s, got %s", m.Checksum, computedChecksum)
|
||||||
}
|
}
|
||||||
if err := unpackPackage(packageFile, name); err != nil {
|
if err := unpackPackage(packageFile, name); err != nil {
|
||||||
return fmt.Errorf("failed to unpack package: %w", err)
|
return fmt.Errorf("failed to unpack package: %w", err)
|
||||||
@ -197,34 +204,53 @@ func InstallPackage(name string) error {
|
|||||||
if err := saveInstalledPackage(name, m); err != nil {
|
if err := saveInstalledPackage(name, m); err != nil {
|
||||||
return fmt.Errorf("failed to save manifest: %w", err)
|
return fmt.Errorf("failed to save manifest: %w", err)
|
||||||
}
|
}
|
||||||
logMessage("Build package " + m.Name + " installed successfully")
|
logTrain("Build package %s installed successfully.", m.Name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return errors.New("unknown package mode")
|
return errors.New("unknown package mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
func RemovePackage(name string) error {
|
func RemovePackage(name string) error {
|
||||||
|
logTrain("Removing package: %s", name)
|
||||||
packagePath := filepath.Join(buildInstallDir, name)
|
packagePath := filepath.Join(buildInstallDir, name)
|
||||||
logMessage("Removing package " + name + " from " + packagePath)
|
|
||||||
if _, err := os.Stat(packagePath); os.IsNotExist(err) {
|
if _, err := os.Stat(packagePath); os.IsNotExist(err) {
|
||||||
return errors.New("package not found")
|
return errors.New("package not found")
|
||||||
}
|
}
|
||||||
|
manifestPath := filepath.Join(packagePath, "manifest.json")
|
||||||
|
data, err := ioutil.ReadFile(manifestPath)
|
||||||
|
if err == nil {
|
||||||
|
var m manifest.Manifest
|
||||||
|
if err := json.Unmarshal(data, &m); err == nil {
|
||||||
|
if len(m.Uninstall) > 0 {
|
||||||
|
for _, cmdStr := range m.Uninstall {
|
||||||
|
logInfo("Executing uninstall command: %s", cmdStr)
|
||||||
|
cmd := exec.Command("sh", "-c", cmdStr)
|
||||||
|
cmd.Dir = packagePath
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("uninstall command '%s' failed: %s", cmdStr, string(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logInfo("Removing directory %s", packagePath)
|
||||||
if err := os.RemoveAll(packagePath); err != nil {
|
if err := os.RemoveAll(packagePath); err != nil {
|
||||||
return fmt.Errorf("failed to remove package: %w", err)
|
return fmt.Errorf("failed to remove package: %w", err)
|
||||||
}
|
}
|
||||||
logMessage("Package " + name + " removed successfully")
|
logTrain("Package %s removed.", name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdatePackage(name string) error {
|
func UpdatePackage(name string) error {
|
||||||
logMessage("Updating package " + name)
|
logTrain("Updating package: %s", name)
|
||||||
if err := RemovePackage(name); err != nil {
|
if err := RemovePackage(name); err != nil {
|
||||||
return fmt.Errorf("failed to remove package for update: %w", err)
|
return fmt.Errorf("failed to remove package for update: %w", err)
|
||||||
}
|
}
|
||||||
if err := InstallPackage(name); err != nil {
|
if err := InstallPackage(name); err != nil {
|
||||||
return fmt.Errorf("failed to install package during update: %w", err)
|
return fmt.Errorf("failed to install package: %w", err)
|
||||||
}
|
}
|
||||||
logMessage("Package " + name + " updated successfully")
|
logTrain("Package %s updated successfully.", name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BIN
pkg/manifest/.DS_Store
vendored
Normal file
BIN
pkg/manifest/.DS_Store
vendored
Normal file
Binary file not shown.
@ -9,6 +9,11 @@ import (
|
|||||||
"train/pkg/config"
|
"train/pkg/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type BinaryInfo struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
Checksum string `json:"checksum"`
|
||||||
|
}
|
||||||
|
|
||||||
type Manifest struct {
|
type Manifest struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
@ -17,7 +22,8 @@ type Manifest struct {
|
|||||||
Checksum string `json:"checksum"`
|
Checksum string `json:"checksum"`
|
||||||
Dependencies []string `json:"dependencies"`
|
Dependencies []string `json:"dependencies"`
|
||||||
Build []string `json:"build"`
|
Build []string `json:"build"`
|
||||||
Binaries map[string]string `json:"binaries"`
|
Binaries map[string]BinaryInfo `json:"binaries"`
|
||||||
|
Uninstall []string `json:"uninstall"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func FetchManifest(name string) (*Manifest, error) {
|
func FetchManifest(name string) (*Manifest, error) {
|
||||||
@ -25,6 +31,12 @@ func FetchManifest(name string) (*Manifest, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if cfg.Repo != "" {
|
||||||
|
m, err := FetchManifestFromRepo(cfg.Repo, name)
|
||||||
|
if err == nil {
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
if url, ok := cfg.Packages[name]; ok {
|
if url, ok := cfg.Packages[name]; ok {
|
||||||
return fetchManifestFromURL(url)
|
return fetchManifestFromURL(url)
|
||||||
}
|
}
|
||||||
@ -38,7 +50,7 @@ func FetchManifest(name string) (*Manifest, error) {
|
|||||||
lastErr = err
|
lastErr = err
|
||||||
}
|
}
|
||||||
if lastErr == nil {
|
if lastErr == nil {
|
||||||
lastErr = errors.New("не найден ни один репозиторий")
|
lastErr = errors.New("no repository found")
|
||||||
}
|
}
|
||||||
return nil, lastErr
|
return nil, lastErr
|
||||||
}
|
}
|
||||||
@ -50,7 +62,7 @@ func fetchManifestFromURL(url string) (*Manifest, error) {
|
|||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
return nil, errors.New("манифест пакета не найден по ссылке " + url)
|
return nil, errors.New("manifest not found at " + url)
|
||||||
}
|
}
|
||||||
data, err := ioutil.ReadAll(resp.Body)
|
data, err := ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
42
pkg/manifest/repo.go
Normal file
42
pkg/manifest/repo.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package manifest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RepoPackage struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Manifest string `json:"manifest"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RepoFile struct {
|
||||||
|
Packages []RepoPackage `json:"packages"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func FetchManifestFromRepo(repoURL, pkgName string) (*Manifest, error) {
|
||||||
|
resp, err := http.Get(repoURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return nil, fmt.Errorf("repo file not found at %s", repoURL)
|
||||||
|
}
|
||||||
|
data, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var rf RepoFile
|
||||||
|
if err := json.Unmarshal(data, &rf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, rp := range rf.Packages {
|
||||||
|
if rp.Name == pkgName {
|
||||||
|
return fetchManifestFromURL(rp.Manifest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("package %s not found in repo", pkgName)
|
||||||
|
}
|
3
pkg/version/current.go
Normal file
3
pkg/version/current.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package version
|
||||||
|
|
||||||
|
const CurrentVersion = "1.1"
|
Loading…
x
Reference in New Issue
Block a user