init commit
This commit is contained in:
commit
4ff821beed
8 files changed
+678
No files matched your search
@@ -0,0 +1 @@
|
|||||||
|
qulay
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package paths
|
||||||
|
|
||||||
|
const ReposUrlsFile = "/etc/qulay/repositories"
|
||||||
|
const ReposDir = "/var/lib/qulay/repos/"
|
||||||
|
const InstalledFile = "/var/lib/qulay/installed"
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
// "archive/tar"
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
// "net/url"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"qulay/config"
|
||||||
|
"codeberg.org/UzbekLinux/uzbekdb-go"
|
||||||
|
|
||||||
|
"github.com/codeclysm/extract/v3"
|
||||||
|
// "github.com/klauspost/compress/zstd"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _name string = filepath.Base(os.Args[0])
|
||||||
|
|
||||||
|
func GetReposUrls() []string {
|
||||||
|
f, err := os.Open(paths.ReposUrlsFile)
|
||||||
|
if err != nil {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
var urls []string
|
||||||
|
scanner := bufio.NewScanner(f)
|
||||||
|
for scanner.Scan() {
|
||||||
|
urls = append(urls, scanner.Text())
|
||||||
|
}
|
||||||
|
return urls
|
||||||
|
}
|
||||||
|
|
||||||
|
func DownloadRepos(urls []string, destDir string) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
var links []string
|
||||||
|
|
||||||
|
for _, link := range urls {
|
||||||
|
link = strings.TrimSpace(link)
|
||||||
|
if link == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
links = append(links, link)
|
||||||
|
}
|
||||||
|
wg.Add(len(links))
|
||||||
|
for _, link := range links {
|
||||||
|
go func(link string) {
|
||||||
|
defer wg.Done()
|
||||||
|
archiveName := path.Base(link)
|
||||||
|
repoName := strings.TrimSuffix(archiveName, ".tar.zst")
|
||||||
|
destArchivePath := filepath.Join(destDir, "tmp", archiveName)
|
||||||
|
destNArchivePath := filepath.Join(destDir, "tmp")
|
||||||
|
destPath := filepath.Join(destDir, paths.ReposDir)
|
||||||
|
fullDestPath := filepath.Join(destDir, paths.ReposDir, repoName)
|
||||||
|
|
||||||
|
os.MkdirAll(destNArchivePath, 0755)
|
||||||
|
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Get(link)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("!! failed to download", link, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
f, err := os.Create(destArchivePath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("!! failed to create file", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(f, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("!! failed to copy", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(":: downloaded", archiveName, "to", destArchivePath)
|
||||||
|
|
||||||
|
err = os.RemoveAll(fullDestPath)
|
||||||
|
fmt.Println(":: removing old repo", fullDestPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("w! failed to delete old repo", fullDestPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(destArchivePath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("!! failed to open archive file", destArchivePath, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = extract.Zstd(context.TODO(), file, destPath, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("!! failed to extract", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(link)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
fmt.Println(":: repo updating ended")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadInstalled(destDir string) map[interface{}][]interface{} {
|
||||||
|
installedFullPath := filepath.Join(destDir, paths.InstalledFile)
|
||||||
|
file, err := os.OpenFile(installedFullPath, os.O_RDONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return map[interface{}][]interface{}{}
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
text, err := io.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return map[interface{}][]interface{}{}
|
||||||
|
}
|
||||||
|
data := uzbekdb.Loads(string(text), "map")
|
||||||
|
if data == nil {
|
||||||
|
return map[interface{}][]interface{}{}
|
||||||
|
}
|
||||||
|
return data.(map[interface{}][]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteInstalled(data map[interface{}][]interface{}, destDir string) error {
|
||||||
|
installedFullPath := filepath.Join(destDir, paths.InstalledFile)
|
||||||
|
file, err := os.OpenFile(installedFullPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
_, err = file.WriteString(uzbekdb.Dumps(data))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsInstalled(name string, destDir string) bool {
|
||||||
|
data := ReadInstalled(destDir)
|
||||||
|
if !strings.Contains(name, "/") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, ok := data[name]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPackageVersion(name string, destDir string) string {
|
||||||
|
data := ReadInstalled(destDir)
|
||||||
|
alo, _ := data[name]
|
||||||
|
if len(alo) > 0 {
|
||||||
|
return alo[0].(string)
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegPackage(name string, version string, destDir string) error {
|
||||||
|
data := ReadInstalled(destDir)
|
||||||
|
_, installed := data[name]
|
||||||
|
if installed {
|
||||||
|
if data[name][0].(string) != version {
|
||||||
|
data[name][0] = version
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data[name] = []interface{}{version}
|
||||||
|
}
|
||||||
|
return WriteInstalled(data, destDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnRegPackage(name string, destDir string) error {
|
||||||
|
data := ReadInstalled(destDir)
|
||||||
|
_, ok := data[name]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("!! cant unregister '%s': package", name, "is not installed")
|
||||||
|
}
|
||||||
|
delete(data, name)
|
||||||
|
return WriteInstalled(data, destDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PurgeRepository(name string, destDir string) error {
|
||||||
|
dirPath := filepath.Join(destDir, paths.ReposDir, name)
|
||||||
|
err := os.RemoveAll(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// func Ensure(args, destDir) error {
|
||||||
|
//
|
||||||
|
// }
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
module qulay
|
||||||
|
|
||||||
|
go 1.25.8
|
||||||
|
|
||||||
|
require (
|
||||||
|
codeberg.org/UzbekLinux/uzbekdb-go v0.0.0-20260324144756-3e62f214b3bb // indirect
|
||||||
|
github.com/codeclysm/extract/v3 v3.1.1 // indirect
|
||||||
|
github.com/h2non/filetype v1.1.3 // indirect
|
||||||
|
github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5 // indirect
|
||||||
|
github.com/klauspost/compress v1.18.5 // indirect
|
||||||
|
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
codeberg.org/UzbekLinux/uzbekdb-go v0.0.0-20260324144756-3e62f214b3bb h1:iUv2IrVCC3MrUy5Q/4kPIk/X5npUCZn1zQe4cUgh6Vk=
|
||||||
|
codeberg.org/UzbekLinux/uzbekdb-go v0.0.0-20260324144756-3e62f214b3bb/go.mod h1:1npzE6+Q2MW4RuDTFcL+V9REv0o9k8LlTRW8jgGqeGA=
|
||||||
|
github.com/codeclysm/extract/v3 v3.1.1 h1:iHZtdEAwSTqPrd+1n4jfhr1qBhUWtHlMTjT90+fJVXg=
|
||||||
|
github.com/codeclysm/extract/v3 v3.1.1/go.mod h1:ZJi80UG2JtfHqJI+lgJSCACttZi++dHxfWuPaMhlOfQ=
|
||||||
|
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
|
||||||
|
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||||
|
github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5 h1:rhqTjzJlm7EbkELJDKMTU7udov+Se0xZkWmugr6zGok=
|
||||||
|
github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
|
||||||
|
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
|
||||||
|
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||||
|
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
|
||||||
|
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Ask(str string) bool {
|
||||||
|
var input string
|
||||||
|
fmt.Printf("-> %s (Y/n): ", str)
|
||||||
|
fmt.Scanln(&input)
|
||||||
|
input = strings.TrimSpace(input)
|
||||||
|
input = strings.ToLower(input)
|
||||||
|
if input != "y" && input != "" && input != "yes" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToString(v interface{}) string {
|
||||||
|
switch val := v.(type) {
|
||||||
|
case int:
|
||||||
|
return strconv.Itoa(val)
|
||||||
|
case float64:
|
||||||
|
return strconv.FormatFloat(val, 'f', -1, 64)
|
||||||
|
case string:
|
||||||
|
return val
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"qulay/database"
|
||||||
|
"qulay/config"
|
||||||
|
"qulay/package"
|
||||||
|
"qulay/helpers"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _name string = filepath.Base(os.Args[0])
|
||||||
|
var _cmds = []string{"h", "+", "-", "g", "s", "p", "u", "r", "q", "v"}
|
||||||
|
var _root_cmds = []string{"+", "-", "u", "i"}
|
||||||
|
|
||||||
|
const _version string = "0.5"
|
||||||
|
const _help string = `{_name}: command-line interface to the Qulay Package Manager
|
||||||
|
Usage:
|
||||||
|
{_name} h -> help menu
|
||||||
|
{_name} + pkg -> download and install package
|
||||||
|
{_name} - pkg -> remove package
|
||||||
|
{_name} g pkg -> get package version
|
||||||
|
{_name} s pkg -> search packages by name
|
||||||
|
{_name} p repo -> purge local repo directory
|
||||||
|
{_name} u -> update all repositories
|
||||||
|
{_name} r -> get all repositories URLs
|
||||||
|
{_name} q -> get all installed packages
|
||||||
|
{_name} v -> get {_name} version
|
||||||
|
Options:
|
||||||
|
:v -> verbose output
|
||||||
|
:a -> ask before doing
|
||||||
|
:nl -> disable recording in databases and logs
|
||||||
|
:nd -> do not download deps
|
||||||
|
:r -> reinstall already installed deps
|
||||||
|
Vars:
|
||||||
|
DESTDIR -> path where the package will be installed/removed`
|
||||||
|
|
||||||
|
func parseArgs(args []string) ([]string, []string, []string) {
|
||||||
|
var cmds, oths, opts []string
|
||||||
|
for _, arg := range args {
|
||||||
|
if strings.HasPrefix(arg, ":") {
|
||||||
|
opts = append(opts, arg)
|
||||||
|
} else if slices.Contains(_cmds, arg) && len(cmds) < 1 {
|
||||||
|
cmds = append(cmds, arg)
|
||||||
|
} else {
|
||||||
|
oths = append(oths, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cmds, oths, opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func printHelp() {
|
||||||
|
r := strings.NewReplacer("{_name}", _name)
|
||||||
|
fmt.Println(r.Replace(_help))
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var args []string = os.Args[1:]
|
||||||
|
cmds, oths, opts := parseArgs(args)
|
||||||
|
|
||||||
|
destDir := os.Getenv("DESTDIR")
|
||||||
|
if destDir == "" {
|
||||||
|
destDir = "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) == 0 || len(cmds) == 0 {
|
||||||
|
printHelp()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Getuid() != 0 && slices.Contains(_root_cmds, cmds[0]) {
|
||||||
|
fmt.Println("!! this command is root-only. run this with root privileges")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.Println(cmds, oths, opts)
|
||||||
|
if cmds[0] == "h" {
|
||||||
|
printHelp()
|
||||||
|
} else if cmds[0] == "+" {
|
||||||
|
if len(oths) < 1 {
|
||||||
|
fmt.Println("!! no package(s) received")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
for _, n := range oths {
|
||||||
|
err := pkg.Install(n, opts, destDir)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("!!", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if cmds[0] == "-" {
|
||||||
|
if len(oths) < 1 {
|
||||||
|
fmt.Println("!! no package(s) received")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
for _, n := range oths {
|
||||||
|
err := pkg.Remove(n, opts, destDir)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("!!", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if cmds[0] == "u" {
|
||||||
|
database.DownloadRepos(database.GetReposUrls(), destDir)
|
||||||
|
} else if cmds[0] == "g" {
|
||||||
|
if len(oths) < 1 {
|
||||||
|
fmt.Println("!! no package(s) received")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
for _, name := range oths {
|
||||||
|
fmt.Println(database.GetPackageVersion(name, destDir))
|
||||||
|
}
|
||||||
|
} else if cmds[0] == "r" {
|
||||||
|
for _, url := range database.GetReposUrls() {
|
||||||
|
fmt.Println(url)
|
||||||
|
}
|
||||||
|
} else if cmds[0] == "q" {
|
||||||
|
for name, version := range database.ReadInstalled(destDir) {
|
||||||
|
fmt.Println(name, "-", version[0])
|
||||||
|
}
|
||||||
|
} else if cmds[0] == "s" {
|
||||||
|
if len(oths) < 1 {
|
||||||
|
pkgs := pkg.GetPackages(destDir)
|
||||||
|
for _, p := range pkgs {
|
||||||
|
if database.IsInstalled(p.Repo + "/" + p.Name, destDir) {
|
||||||
|
fmt.Printf("[+] %s/%s - %v - %v\n", p.Repo, p.Name, p.Data[0], p.Data[1])
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" %s/%s - %v - %v\n", p.Repo, p.Name, p.Data[0], p.Data[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, n := range oths {
|
||||||
|
pkgs := pkg.FindPackages(n, destDir)
|
||||||
|
for _, p := range pkgs {
|
||||||
|
if database.IsInstalled(p.Repo + "/" + p.Name, destDir) {
|
||||||
|
fmt.Printf("[+] %s/%s - %v - %v\n", p.Repo, p.Name, p.Data[0], p.Data[1])
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" %s/%s - %v - %v\n", p.Repo, p.Name, p.Data[0], p.Data[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if cmds[0] == "p" {
|
||||||
|
if slices.Contains(opts, ":a") {
|
||||||
|
if !helpers.Ask("do you want purge repo(s)?") {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(oths) >= 1 {
|
||||||
|
for _, n := range oths {
|
||||||
|
err := database.PurgeRepository(n, destDir)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("!!", err)
|
||||||
|
os.Exit(1)
|
||||||
|
} else {
|
||||||
|
fmt.Println(":: deleted", n, "repository in", paths.ReposDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if cmds[0] == "v" {
|
||||||
|
fmt.Println(_version)
|
||||||
|
} else {
|
||||||
|
fmt.Println("w! unknown command:", cmds[0])
|
||||||
|
printHelp()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
package pkg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"bufio"
|
||||||
|
"math/rand"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
"errors"
|
||||||
|
// "slices"
|
||||||
|
// "strconv"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"qulay/database"
|
||||||
|
"qulay/helpers"
|
||||||
|
"qulay/config"
|
||||||
|
|
||||||
|
"codeberg.org/UzbekLinux/uzbekdb-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _name string = filepath.Base(os.Args[0])
|
||||||
|
|
||||||
|
type Package struct {
|
||||||
|
Name string
|
||||||
|
Repo string
|
||||||
|
Data []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func randStr(n int) string {
|
||||||
|
b := make([]byte, n)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = byte(rand.Intn(26) + 'a')
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func randomTempDir(destDir string) string {
|
||||||
|
tempDir := "qulay_" + randStr(10)
|
||||||
|
tempDirPath := filepath.Join(destDir, tempDir)
|
||||||
|
os.MkdirAll(tempDirPath, 0755)
|
||||||
|
return tempDirPath
|
||||||
|
}
|
||||||
|
|
||||||
|
func readDepends(path string) []string {
|
||||||
|
dependsPath := filepath.Join(path, "depends")
|
||||||
|
dependsPath2 := filepath.Join(path, "depends.uz")
|
||||||
|
f, err := os.Open(dependsPath)
|
||||||
|
if err != nil {
|
||||||
|
f, err = os.Open(dependsPath2)
|
||||||
|
if err != nil {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
var depends []string
|
||||||
|
scanner := bufio.NewScanner(f)
|
||||||
|
for scanner.Scan() {
|
||||||
|
depends = append(depends, scanner.Text())
|
||||||
|
}
|
||||||
|
return depends
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNewFormat(path string) bool {
|
||||||
|
_, err := os.Stat(filepath.Join(path, "data"))
|
||||||
|
return !os.IsNotExist(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPackages(destDir string) []Package {
|
||||||
|
var found []Package
|
||||||
|
entries, _ := os.ReadDir(filepath.Join(destDir, paths.ReposDir))
|
||||||
|
for _, e := range entries {
|
||||||
|
if !e.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data, err := os.ReadFile(filepath.Join(destDir, paths.ReposDir, e.Name(), "pkgs.uz"))
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pkgs, ok := uzbekdb.Loads(string(data), "map").(map[interface{}][]interface{})
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for name, val := range pkgs {
|
||||||
|
found = append(found, Package{fmt.Sprintf("%v", name), e.Name(), val})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slices.SortFunc(found, func(a, b Package) int {
|
||||||
|
if n := strings.Compare(a.Repo, b.Repo); n != 0 {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
return strings.Compare(a.Name, b.Name)
|
||||||
|
})
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindPackages(name string, destDir string) []Package {
|
||||||
|
var found []Package
|
||||||
|
var packages = GetPackages(destDir)
|
||||||
|
var splitName = false
|
||||||
|
var repo string
|
||||||
|
|
||||||
|
if strings.Contains(name, "/") {
|
||||||
|
splitName = true
|
||||||
|
parts := strings.SplitN(name, "/", 2)
|
||||||
|
repo = parts[0]
|
||||||
|
name = parts[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range packages {
|
||||||
|
if splitName {
|
||||||
|
if e.Name == name && e.Repo == repo {
|
||||||
|
found = append(found, e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if e.Name == name {
|
||||||
|
found = append(found, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPackage(name string, destDir string) (*Package, error) {
|
||||||
|
var pkgs []Package
|
||||||
|
var pkg Package
|
||||||
|
if strings.Contains(name, "/") {
|
||||||
|
parts := strings.SplitN(name, "/", 2)
|
||||||
|
repo := parts[0]
|
||||||
|
name = parts[1]
|
||||||
|
pkgs = FindPackages(repo + "/" + name, destDir)
|
||||||
|
if len(pkgs) == 0 {
|
||||||
|
return nil, errors.New("no packages found with name " + name + " in " + repo + " repository")
|
||||||
|
} else if len(pkgs) == 1 {
|
||||||
|
pkg = pkgs[0]
|
||||||
|
} else {
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
fmt.Printf("%s/%s - %v - %v\n", pkg.Repo, pkg.Name, pkg.Data[0], pkg.Data[1])
|
||||||
|
}
|
||||||
|
return nil, errors.New("? found multiple packages with name " + name + " in " + repo + " repository")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pkgs = FindPackages(name, destDir)
|
||||||
|
if len(pkgs) == 0 {
|
||||||
|
return nil, errors.New("no packages found with name " + name + " in repositories")
|
||||||
|
} else if len(pkgs) > 1 {
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
fmt.Printf("%s/%s - %v - %v\n", pkg.Repo, pkg.Name, pkg.Data[0], pkg.Data[1])
|
||||||
|
}
|
||||||
|
return nil, errors.New("found multiple packages with name " + name + " in repositories")
|
||||||
|
} else {
|
||||||
|
pkg = pkgs[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &pkg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Install(name string, args []string, destDir string) error {
|
||||||
|
// verbose := slices.Contains(args, ":v")
|
||||||
|
reinstall := slices.Contains(args, ":r")
|
||||||
|
disableDeps := slices.Contains(args, ":nd")
|
||||||
|
|
||||||
|
pkg, err := getPackage(name, destDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgPath := filepath.Join(destDir, paths.ReposDir, pkg.Repo, "packages", pkg.Name)
|
||||||
|
depends := readDepends(pkgPath)
|
||||||
|
if !disableDeps {
|
||||||
|
if len(depends) != 0 {
|
||||||
|
fmt.Println(helpers.ToString(len(depends)) + " depends for " + pkg.Repo + "/" + pkg.Name + ":")
|
||||||
|
for i, n := range depends {
|
||||||
|
if n == pkg.Name || n == pkg.Repo + "/" + pkg.Name {
|
||||||
|
return errors.New("broken depends in package " + n)
|
||||||
|
}
|
||||||
|
fmt.Printf("%v. %s - %v", i, n, database.GetPackageVersion(n, destDir))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if slices.Contains(args, ":a") {
|
||||||
|
if !helpers.Ask("install " + pkg.Repo + "/" + pkg.Name + " with version " + helpers.ToString(pkg.Data[1])) {
|
||||||
|
return errors.New("canceled by user")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !disableDeps {
|
||||||
|
for _, dep := range depends {
|
||||||
|
if !database.IsInstalled(dep, destDir) || reinstall {
|
||||||
|
err := Install(dep, args, destDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isNewFormat(pkgPath) {
|
||||||
|
cmd := exec.Command("sh", "install.sh")
|
||||||
|
cmd.Dir = pkgPath
|
||||||
|
cmd.Env = os.Environ()
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = database.RegPackage(pkg.Repo + "/" + pkg.Name, helpers.ToString(pkg.Data[1]), destDir)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("failed to register package in installed database: " + err.Error())
|
||||||
|
}
|
||||||
|
fmt.Println(":: " + pkg.Repo + "/" + pkg.Name + " (" + helpers.ToString(pkg.Data[1]) + ") installed")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Remove(name string, args []string, destDir string) error {
|
||||||
|
pkg, err := getPackage(name, destDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if slices.Contains(args, ":a") {
|
||||||
|
if !helpers.Ask("remove " + pkg.Repo + "/" + pkg.Name + " with repo version " + helpers.ToString(pkg.Data[1]) +
|
||||||
|
" and local version " + database.GetPackageVersion(pkg.Repo + "/" + pkg.Name, destDir) + "?") {
|
||||||
|
return errors.New("canceled by user")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgPath := filepath.Join(destDir, paths.ReposDir, pkg.Repo, "packages", pkg.Name)
|
||||||
|
|
||||||
|
if !isNewFormat(pkgPath) {
|
||||||
|
cmd := exec.Command("sh", "remove.sh")
|
||||||
|
cmd.Dir = pkgPath
|
||||||
|
cmd.Env = os.Environ()
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = database.UnRegPackage(pkg.Repo + "/" + pkg.Name, destDir)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("failed to unregister package in installed database: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(":: " + pkg.Repo + "/" + pkg.Name + " (" + database.GetPackageVersion(pkg.Repo + "/" + pkg.Name, destDir) + ") removed")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Reference in new issue
Block a user