Compare commits
No commits in common. "main" and "master" have entirely different histories.
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,3 @@
|
||||
installed*
|
||||
repo
|
||||
train
|
||||
pkg/*/*.save
|
||||
train-pkg*
|
||||
|
31
README.md
31
README.md
@ -1,31 +0,0 @@
|
||||
# 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
|
||||
```
|
@ -3,6 +3,7 @@ package cmd
|
||||
import (
|
||||
"fmt"
|
||||
"train/pkg/installer"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -19,4 +20,4 @@ var infoCmd = &cobra.Command{
|
||||
}
|
||||
fmt.Printf("Информация о пакете %s:\n%s\n", pkgName, info)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -1,82 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"io"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var installAllCmd = &cobra.Command{
|
||||
Use: "install-all",
|
||||
Short: "Копирует бинарник и config.json в ~/.rails",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
fmt.Printf("Ошибка получения домашней директории: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
railsDir := filepath.Join(home, ".rails")
|
||||
binDir := filepath.Join(railsDir, "bin")
|
||||
configDir := filepath.Join(railsDir, "config")
|
||||
if err := os.MkdirAll(binDir, 0755); err != nil {
|
||||
fmt.Printf("Ошибка создания директории %s: %v\n", binDir, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := os.MkdirAll(configDir, 0755); err != nil {
|
||||
fmt.Printf("Ошибка создания директории %s: %v\n", configDir, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
exePath, err := os.Executable()
|
||||
if err != nil {
|
||||
fmt.Printf("Ошибка получения пути к бинарнику: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
targetBin := filepath.Join(binDir, "train")
|
||||
srcBin, err := os.Open(exePath)
|
||||
if err != nil {
|
||||
fmt.Printf("Ошибка открытия бинарника: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer srcBin.Close()
|
||||
dstBin, err := os.Create(targetBin)
|
||||
if err != nil {
|
||||
fmt.Printf("Ошибка создания файла бинарника: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer dstBin.Close()
|
||||
if _, err = io.Copy(dstBin, srcBin); err != nil {
|
||||
fmt.Printf("Ошибка копирования бинарника: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err = os.Chmod(targetBin, 0755); err != nil {
|
||||
fmt.Printf("Ошибка установки прав для бинарника: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Бинарник скопирован в %s\n", targetBin)
|
||||
sourceConfig := "./config.json"
|
||||
targetConfig := filepath.Join(configDir, "config.json")
|
||||
srcConfig, err := os.Open(sourceConfig)
|
||||
if err != nil {
|
||||
fmt.Printf("Ошибка открытия файла конфигурации: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer srcConfig.Close()
|
||||
dstConfig, err := os.Create(targetConfig)
|
||||
if err != nil {
|
||||
fmt.Printf("Ошибка создания файла конфигурации: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer dstConfig.Close()
|
||||
if _, err = io.Copy(dstConfig, srcConfig); err != nil {
|
||||
fmt.Printf("Ошибка копирования файла конфигурации: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Файл конфигурации скопирован в %s\n", targetConfig)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(installAllCmd)
|
||||
}
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"train/pkg/installer"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -20,4 +21,4 @@ var installCmd = &cobra.Command{
|
||||
}
|
||||
fmt.Printf("Пакет %s успешно установлен.\n", pkgName)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package cmd
|
||||
import (
|
||||
"fmt"
|
||||
"train/pkg/installer"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -20,4 +21,4 @@ var listCmd = &cobra.Command{
|
||||
fmt.Println(" -", p)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"train/pkg/installer"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -20,4 +21,4 @@ var removeCmd = &cobra.Command{
|
||||
}
|
||||
fmt.Printf("Пакет %s успешно удалён.\n", pkgName)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"train/pkg/config"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -60,4 +61,4 @@ func init() {
|
||||
repoCmd.AddCommand(repoAddCmd)
|
||||
repoCmd.AddCommand(repoRemoveCmd)
|
||||
repoCmd.AddCommand(repoListCmd)
|
||||
}
|
||||
}
|
||||
|
12
cmd/root.go
12
cmd/root.go
@ -3,19 +3,13 @@ package cmd
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "train",
|
||||
Short: "Train - Лёгкий пакетный менеджер",
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
if runtime.GOOS == "windows" {
|
||||
fmt.Println("[WARNING] Train работает на Windows в режиме совместимости. Гарантий работы нет.")
|
||||
}
|
||||
},
|
||||
Short: "Train - легковесный пакетный менеджер",
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
@ -31,5 +25,5 @@ func init() {
|
||||
rootCmd.AddCommand(updateCmd)
|
||||
rootCmd.AddCommand(listCmd)
|
||||
rootCmd.AddCommand(infoCmd)
|
||||
rootCmd.AddCommand(repoCmd)
|
||||
}
|
||||
rootCmd.AddCommand(repoCmd)
|
||||
}
|
||||
|
@ -1,63 +0,0 @@
|
||||
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)
|
||||
}
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"train/pkg/installer"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -20,4 +21,4 @@ var updateCmd = &cobra.Command{
|
||||
}
|
||||
fmt.Printf("Пакет %s успешно обновлён.\n", pkgName)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +0,0 @@
|
||||
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,6 @@
|
||||
{
|
||||
"repo": "https://xn--80abmlgju2eo3byb.xn--p1ai/repo/repo.json",
|
||||
"repositories": [],
|
||||
"packages": {}
|
||||
"repositories": [
|
||||
"http://xn--80abmlgju2eo3byb.xn--p1ai/sklad/"
|
||||
],
|
||||
}
|
||||
}
|
||||
|
2
go.mod
2
go.mod
@ -2,7 +2,7 @@ module train
|
||||
|
||||
go 1.18
|
||||
|
||||
require github.com/spf13/cobra v1.7.0
|
||||
require github.com/spf13/cobra v1.7.0 // или актуальная версия
|
||||
|
||||
require (
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
|
6
main.go
6
main.go
@ -1,7 +1,9 @@
|
||||
package main
|
||||
|
||||
import "train/cmd"
|
||||
import (
|
||||
"train/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd.Execute()
|
||||
}
|
||||
}
|
||||
|
@ -1,129 +0,0 @@
|
||||
package autoupdate
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
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 "", "", fmt.Errorf("failed to download tarball: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
tmpTarball, err := ioutil.TempFile("", "source_*.tar.gz")
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("failed to create temp file: %w", err)
|
||||
}
|
||||
_, err = io.Copy(tmpTarball, resp.Body)
|
||||
if err != nil {
|
||||
tmpTarball.Close()
|
||||
return "", "", fmt.Errorf("failed to write tarball: %w", err)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
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 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
|
||||
}
|
52
pkg/cache/cache.go
vendored
52
pkg/cache/cache.go
vendored
@ -1,52 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"train/pkg/paths"
|
||||
)
|
||||
|
||||
var cacheDir = filepath.Join(paths.BaseDir(), "cache")
|
||||
|
||||
func GetCachedFile(url string) (string, error) {
|
||||
if err := os.MkdirAll(cacheDir, os.ModePerm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
fileName := filepath.Join(cacheDir, filepath.Base(url))
|
||||
if _, err := os.Stat(fileName); err == nil {
|
||||
return fileName, nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func SaveToCache(url string, data []byte) (string, error) {
|
||||
if err := os.MkdirAll(cacheDir, os.ModePerm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
fileName := filepath.Join(cacheDir, filepath.Base(url))
|
||||
if err := ioutil.WriteFile(fileName, data, 0644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
fmt.Printf("Сохранено в кэш: %s\n", fileName)
|
||||
return fileName, nil
|
||||
}
|
||||
|
||||
func DownloadWithCache(url string) (string, error) {
|
||||
if cached, err := GetCachedFile(url); err == nil && cached != "" {
|
||||
return cached, nil
|
||||
}
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return SaveToCache(url, data)
|
||||
}
|
@ -2,43 +2,26 @@ package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Repo string `json:"repo"`
|
||||
Repositories []string `json:"repositories"`
|
||||
Packages map[string]string `json:"packages"`
|
||||
Repositories []string `json:"repositories"`
|
||||
}
|
||||
|
||||
var configFile string
|
||||
|
||||
func init() {
|
||||
home, err := os.UserHomeDir()
|
||||
if err == nil {
|
||||
railsConfig := filepath.Join(home, ".rails", "config", "config.json")
|
||||
if _, err := os.Stat(railsConfig); err == nil {
|
||||
configFile = railsConfig
|
||||
return
|
||||
}
|
||||
}
|
||||
configFile = "./config.json"
|
||||
}
|
||||
var configFile = "./config.json"
|
||||
|
||||
func LoadConfig() (*Config, error) {
|
||||
if _, err := os.Stat(configFile); os.IsNotExist(err) {
|
||||
cfg := &Config{
|
||||
Repo: "",
|
||||
Repositories: []string{},
|
||||
Packages: make(map[string]string),
|
||||
}
|
||||
cfg := &Config{Repositories: []string{}}
|
||||
if err := SaveConfig(cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(configFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -65,7 +48,7 @@ func AddRepository(repo string) error {
|
||||
}
|
||||
for _, r := range cfg.Repositories {
|
||||
if r == repo {
|
||||
return nil
|
||||
return errors.New("репозиторий уже добавлен")
|
||||
}
|
||||
}
|
||||
cfg.Repositories = append(cfg.Repositories, repo)
|
||||
@ -78,11 +61,17 @@ func RemoveRepository(repo string) error {
|
||||
return err
|
||||
}
|
||||
newRepos := []string{}
|
||||
found := false
|
||||
for _, r := range cfg.Repositories {
|
||||
if r != repo {
|
||||
newRepos = append(newRepos, r)
|
||||
if r == repo {
|
||||
found = true
|
||||
continue
|
||||
}
|
||||
newRepos = append(newRepos, r)
|
||||
}
|
||||
if !found {
|
||||
return errors.New("репозиторий не найден")
|
||||
}
|
||||
cfg.Repositories = newRepos
|
||||
return SaveConfig(cfg)
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
package dependencies
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"train/pkg/installer"
|
||||
)
|
||||
|
||||
func InstallDependencies(deps []string) error {
|
||||
for _, dep := range deps {
|
||||
fmt.Printf("Устанавливаем зависимость: %s\n", dep)
|
||||
if err := installer.InstallPackage(dep); err != nil {
|
||||
return fmt.Errorf("не удалось установить зависимость %s: %v", dep, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -18,24 +18,14 @@ import (
|
||||
"strings"
|
||||
|
||||
"train/pkg/manifest"
|
||||
"train/pkg/paths"
|
||||
)
|
||||
|
||||
var (
|
||||
binInstallDir = filepath.Join(paths.BaseDir(), "installed_bins")
|
||||
buildInstallDir = filepath.Join(paths.BaseDir(), "installed_packages")
|
||||
binInstallDir = "./installed_bins" // for binary
|
||||
buildInstallDir = "./installed_packages" // for build"
|
||||
installDir = buildInstallDir // for func
|
||||
)
|
||||
|
||||
func logInfo(format string, args ...interface{}) {
|
||||
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) {
|
||||
data, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
@ -46,52 +36,55 @@ func computeChecksum(filePath string) (string, error) {
|
||||
}
|
||||
|
||||
func downloadFile(url string) (string, error) {
|
||||
logInfo("Downloading %s", url)
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
tmpFile, err := ioutil.TempFile("", "package_")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, err = io.Copy(tmpFile, resp.Body)
|
||||
if err != nil {
|
||||
tmpFile.Close()
|
||||
return "", err
|
||||
}
|
||||
tmpFile.Close()
|
||||
logInfo("File downloaded to temporary location")
|
||||
return tmpFile.Name(), nil
|
||||
}
|
||||
|
||||
func unpackPackage(filePath, packageName string) error {
|
||||
logInfo("Unpacking package into %s", filepath.Join(buildInstallDir, packageName))
|
||||
destDir := filepath.Join(buildInstallDir, packageName)
|
||||
if err := os.MkdirAll(destDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Open(filePath)
|
||||
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
|
||||
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 {
|
||||
@ -107,28 +100,27 @@ func unpackPackage(filePath, packageName string) error {
|
||||
return err
|
||||
}
|
||||
outFile.Close()
|
||||
default:
|
||||
|
||||
}
|
||||
}
|
||||
logInfo("Unpacking completed")
|
||||
return nil
|
||||
}
|
||||
|
||||
func executeBuildCommands(commands []string, packageName string) error {
|
||||
packagePath := filepath.Join(buildInstallDir, packageName)
|
||||
for _, commandStr := range commands {
|
||||
logInfo("Executing build command: %s", commandStr)
|
||||
cmd := exec.Command("sh", "-c", commandStr)
|
||||
cmd.Dir = packagePath
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("command '%s' failed: %s", commandStr, string(output))
|
||||
return fmt.Errorf("команда '%s' завершилась с ошибкой: %s", commandStr, string(output))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func saveInstalledPackage(name string, m *manifest.Manifest) error {
|
||||
logInfo("Saving manifest to %s", filepath.Join(buildInstallDir, name))
|
||||
packagePath := filepath.Join(buildInstallDir, name)
|
||||
if err := os.MkdirAll(packagePath, os.ModePerm); err != nil {
|
||||
return err
|
||||
@ -140,31 +132,35 @@ func saveInstalledPackage(name string, m *manifest.Manifest) error {
|
||||
return ioutil.WriteFile(filepath.Join(packagePath, "manifest.json"), jsonData, 0644)
|
||||
}
|
||||
|
||||
func removeInstalledPackageRecord(name string) {
|
||||
}
|
||||
|
||||
func InstallPackage(name string) error {
|
||||
logTrain("Installing package: %s", name)
|
||||
m, err := manifest.FetchManifest(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch manifest: %w", err)
|
||||
return fmt.Errorf("не удалось получить манифест: %w", err)
|
||||
}
|
||||
|
||||
if strings.ToLower(m.Mode) == "bin" {
|
||||
logInfo("Installing in binary mode")
|
||||
platformKey := runtime.GOOS + "_" + runtime.GOARCH
|
||||
binaryInfo, ok := m.Binaries[platformKey]
|
||||
binaryURL, ok := m.Binaries[platformKey]
|
||||
if !ok {
|
||||
return fmt.Errorf("no binary available for platform %s", platformKey)
|
||||
return fmt.Errorf("нет бинарного файла для платформы %s", platformKey)
|
||||
}
|
||||
packageFile, err := downloadFile(binaryInfo.URL)
|
||||
packageFile, err := downloadFile(binaryURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to download binary: %w", err)
|
||||
return fmt.Errorf("не удалось скачать бинарник: %w", err)
|
||||
}
|
||||
defer os.Remove(packageFile)
|
||||
computedChecksum, err := computeChecksum(packageFile)
|
||||
|
||||
checksum, err := computeChecksum(packageFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to compute checksum: %w", err)
|
||||
return fmt.Errorf("не удалось вычислить контрольную сумму: %w", err)
|
||||
}
|
||||
if computedChecksum != binaryInfo.Checksum {
|
||||
return fmt.Errorf("checksum mismatch: expected %s, got %s", binaryInfo.Checksum, computedChecksum)
|
||||
if checksum != m.Checksum {
|
||||
return fmt.Errorf("контрольная сумма не совпадает, ожидалось %s, получено %s", m.Checksum, checksum)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(binInstallDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -176,81 +172,61 @@ func InstallPackage(name string) error {
|
||||
if err := ioutil.WriteFile(destPath, data, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
logInfo("Binary package %s installed in %s", m.Name, destPath)
|
||||
logTrain("Package %s successfully installed.", m.Name)
|
||||
fmt.Printf("Бинарный пакет %s установлен в %s\n", m.Name, destPath)
|
||||
return nil
|
||||
} else if strings.ToLower(m.Mode) == "build" {
|
||||
logInfo("Installing in build mode")
|
||||
packageFile, err := downloadFile(m.Source)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to download package: %w", err)
|
||||
return fmt.Errorf("не удалось скачать пакет: %w", err)
|
||||
}
|
||||
defer os.Remove(packageFile)
|
||||
computedChecksum, err := computeChecksum(packageFile)
|
||||
|
||||
checksum, err := computeChecksum(packageFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to compute checksum: %w", err)
|
||||
return fmt.Errorf("не удалось вычислить контрольную сумму: %w", err)
|
||||
}
|
||||
if computedChecksum != m.Checksum {
|
||||
return fmt.Errorf("checksum mismatch: expected %s, got %s", m.Checksum, computedChecksum)
|
||||
if checksum != m.Checksum {
|
||||
return fmt.Errorf("контрольная сумма не совпадает, ожидалось %s, получено %s", m.Checksum, checksum)
|
||||
}
|
||||
|
||||
if err := unpackPackage(packageFile, name); err != nil {
|
||||
return fmt.Errorf("failed to unpack package: %w", err)
|
||||
return fmt.Errorf("не удалось распаковать пакет: %w", err)
|
||||
}
|
||||
|
||||
if len(m.Build) > 0 {
|
||||
if err := executeBuildCommands(m.Build, name); err != nil {
|
||||
return fmt.Errorf("build command error: %w", err)
|
||||
return fmt.Errorf("ошибка выполнения команд сборки: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := saveInstalledPackage(name, m); err != nil {
|
||||
return fmt.Errorf("failed to save manifest: %w", err)
|
||||
return fmt.Errorf("не удалось сохранить информацию об установленном пакете: %w", err)
|
||||
}
|
||||
logTrain("Build package %s installed successfully.", m.Name)
|
||||
return nil
|
||||
}
|
||||
return errors.New("unknown package mode")
|
||||
|
||||
return errors.New("неизвестный режим пакета")
|
||||
}
|
||||
|
||||
func RemovePackage(name string) error {
|
||||
logTrain("Removing package: %s", name)
|
||||
packagePath := filepath.Join(buildInstallDir, name)
|
||||
if _, err := os.Stat(packagePath); os.IsNotExist(err) {
|
||||
return errors.New("package not found")
|
||||
return errors.New("пакет не найден")
|
||||
}
|
||||
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 {
|
||||
return fmt.Errorf("failed to remove package: %w", err)
|
||||
return fmt.Errorf("не удалось удалить пакет: %w", err)
|
||||
}
|
||||
logTrain("Package %s removed.", name)
|
||||
removeInstalledPackageRecord(name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdatePackage(name string) error {
|
||||
logTrain("Updating package: %s", name)
|
||||
if err := RemovePackage(name); err != nil {
|
||||
return fmt.Errorf("failed to remove package for update: %w", err)
|
||||
return fmt.Errorf("не удалось удалить пакет для обновления: %w", err)
|
||||
}
|
||||
if err := InstallPackage(name); err != nil {
|
||||
return fmt.Errorf("failed to install package: %w", err)
|
||||
return fmt.Errorf("не удалось установить пакет: %w", err)
|
||||
}
|
||||
logTrain("Package %s updated successfully.", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -275,4 +251,4 @@ func GetPackageInfo(name string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
}
|
||||
|
BIN
pkg/manifest/.DS_Store
vendored
BIN
pkg/manifest/.DS_Store
vendored
Binary file not shown.
@ -9,21 +9,16 @@ import (
|
||||
"train/pkg/config"
|
||||
)
|
||||
|
||||
type BinaryInfo struct {
|
||||
URL string `json:"url"`
|
||||
Checksum string `json:"checksum"`
|
||||
}
|
||||
|
||||
type Manifest struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Mode string `json:"mode"`
|
||||
Source string `json:"source"`
|
||||
Checksum string `json:"checksum"`
|
||||
Dependencies []string `json:"dependencies"`
|
||||
Build []string `json:"build"`
|
||||
Binaries map[string]BinaryInfo `json:"binaries"`
|
||||
Uninstall []string `json:"uninstall"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Mode string `json:"mode"`
|
||||
Source string `json:"source"`
|
||||
Checksum string `json:"checksum"`
|
||||
Dependencies []string `json:"dependencies"`
|
||||
Build []string `json:"build"`
|
||||
Binaries map[string]string `json:"binaries"`
|
||||
runtime.GOOS+"_"+runtime.GOARCH
|
||||
}
|
||||
|
||||
func FetchManifest(name string) (*Manifest, error) {
|
||||
@ -31,46 +26,33 @@ func FetchManifest(name string) (*Manifest, error) {
|
||||
if err != nil {
|
||||
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 {
|
||||
return fetchManifestFromURL(url)
|
||||
}
|
||||
var lastErr error
|
||||
for _, repo := range cfg.Repositories {
|
||||
url := fmt.Sprintf("%s/%s/manifest.json", repo, name)
|
||||
m, err := fetchManifestFromURL(url)
|
||||
if err == nil {
|
||||
return m, nil
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
lastErr = err
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
lastErr = errors.New("пакет не найден в репозитории " + repo)
|
||||
continue
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
var m Manifest
|
||||
if err := json.Unmarshal(data, &m); err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
if lastErr == nil {
|
||||
lastErr = errors.New("no repository found")
|
||||
lastErr = errors.New("не найден ни один репозиторий")
|
||||
}
|
||||
return nil, lastErr
|
||||
}
|
||||
|
||||
func fetchManifestFromURL(url string) (*Manifest, error) {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, errors.New("manifest not found at " + url)
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var m Manifest
|
||||
if err := json.Unmarshal(data, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &m, nil
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
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)
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package paths
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func BaseDir() string {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "."
|
||||
}
|
||||
return filepath.Join(home, ".rails")
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"plugin"
|
||||
)
|
||||
|
||||
func LoadPlugin(path string) (*plugin.Plugin, error) {
|
||||
p, err := plugin.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("Плагин загружен: %s\n", path)
|
||||
return p, nil
|
||||
}
|
BIN
pkg/security/.DS_Store
vendored
BIN
pkg/security/.DS_Store
vendored
Binary file not shown.
@ -1,14 +0,0 @@
|
||||
package security
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func VerifySignature(filePath, sigPath string) error {
|
||||
out, err := exec.Command("gpg", "--verify", sigPath, filePath).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("gpg verification failed: %s", string(out))
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
package version
|
||||
|
||||
const CurrentVersion = "1.1"
|
@ -1,43 +0,0 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"train/pkg/paths"
|
||||
)
|
||||
|
||||
type VersionInfo struct {
|
||||
Package string `json:"package"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
var versionDir = filepath.Join(paths.BaseDir(), "versions")
|
||||
|
||||
func SaveVersionInfo(pkgName, ver string) error {
|
||||
if err := os.MkdirAll(versionDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
info := VersionInfo{Package: pkgName, Version: ver}
|
||||
data, err := json.MarshalIndent(info, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filePath := filepath.Join(versionDir, pkgName+".json")
|
||||
return ioutil.WriteFile(filePath, data, 0644)
|
||||
}
|
||||
|
||||
func GetVersionInfo(pkgName string) (*VersionInfo, error) {
|
||||
filePath := filepath.Join(versionDir, pkgName+".json")
|
||||
data, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var info VersionInfo
|
||||
if err := json.Unmarshal(data, &info); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &info, nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user