package pkg import ( "bufio" "errors" "fmt" "slices" "strings" // "slices" // "strconv" "os" "os/exec" "path/filepath" paths "qulay/config" "qulay/database" "qulay/helpers" "codeberg.org/UzbekLinux/uzbekdb-go" ) var _name string = filepath.Base(os.Args[0]) type Package struct { Name string Repo string Data []interface{} } func randomTempDir(destDir string) string { tempDir := "qulay_" + helpers.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")) 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) } depPkgs := FindPackages(n, destDir) v := "not found" if len(depPkgs) > 0 { v = fmt.Sprintf("%v", depPkgs[0].Data[1]) } fmt.Printf("%v. %s - %v\n", i+1, n, v) } } } 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 { depPkg, err := getPackage(dep, destDir) if err != nil { return err } fullName := depPkg.Repo + "/" + depPkg.Name if !database.IsInstalled(fullName, destDir) || reinstall { err := Install(fullName, args, destDir) if err != nil { return err } } } } if !isNewFormat(pkgPath) { cmd := exec.Command("sh", "install.sh") cmd.Dir = pkgPath cmd.Env = append(os.Environ(), "DESTDIR="+destDir) 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 err } v := helpers.ToString(pkg.Data[1]) fmt.Println(":: " + pkg.Repo + "/" + pkg.Name + " (" + v + ") removed") return nil }