From 8a1670519ce54710984b8ec87c1f81a937189c3e Mon Sep 17 00:00:00 2001 From: aaron Date: Sat, 2 May 2026 21:25:53 +0200 Subject: [PATCH] added basic cache functionality. Note that this works only with the install command, and stores only the PKGBUILD files. --- cmd/install.go | 91 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 13 deletions(-) diff --git a/cmd/install.go b/cmd/install.go index 4cf813a..e40dd5c 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -2,12 +2,17 @@ package cmd import ( "fmt" + "io" "os" "os/exec" + "path/filepath" + "time" "github.com/spf13/cobra" ) +const cacheBase = "/var/cache/apac/pkgs" + var installCmd = &cobra.Command{ Use: "install", Short: "installs a package", @@ -15,36 +20,96 @@ var installCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { if len(args) == 0 { fmt.Fprintln(os.Stderr, "error: package name required") - return + os.Exit(1) } pkg := args[0] url := "https://aur.archlinux.org/" + pkg - // git clone - fmt.Println("cloning:", url) - out, err := exec.Command("git", "clone", url).CombinedOutput() - if err != nil { - fmt.Fprintf(os.Stderr, "git clone failed: %v\n%s\n", err, string(out)) - return + // ensure cache directory exists (use sudo mkdir -p so permissions can be elevated) + mkdirCmd := exec.Command("sudo", "mkdir", "-p", cacheBase) + mkdirCmd.Stdin = os.Stdin + mkdirCmd.Stdout = os.Stdout + mkdirCmd.Stderr = os.Stderr + if err := mkdirCmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "failed to create cache dir %s: %v\n", cacheBase, err) + os.Exit(1) } - // makepkg -si inside package dir + cacheDir := filepath.Join(cacheBase, pkg) + tmpDir := filepath.Join(os.TempDir(), "apac-build-"+pkg+"-"+time.Now().Format("20060102-150405")) + + // clone into cache if missing; DO NOT pull/update existing cache + if _, err := os.Stat(cacheDir); os.IsNotExist(err) { + fmt.Println("cloning into cache:", url, "->", cacheDir) + cloneCmd := exec.Command("sudo", "git", "clone", url, cacheDir) + cloneCmd.Stdin = os.Stdin + cloneCmd.Stdout = os.Stdout + cloneCmd.Stderr = os.Stderr + if err := cloneCmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "git clone failed: %v\n", err) + os.Exit(1) + } + } else if err != nil { + fmt.Fprintf(os.Stderr, "stat cache dir failed: %v\n", err) + os.Exit(1) + } else { + fmt.Println("using cached repo:", cacheDir) + } + + // copy cached repo into a temporary build directory + if err := copyDir(cacheDir, tmpDir); err != nil { + fmt.Fprintf(os.Stderr, "failed to copy to build dir: %v\n", err) + os.Exit(1) + } + defer os.RemoveAll(tmpDir) + + // run makepkg in tmpDir (makepkg will handle permissions) fmt.Println("building/installing:", pkg) buildCmd := exec.Command("makepkg", "-si", "--noconfirm") - buildCmd.Dir = pkg + buildCmd.Dir = tmpDir buildCmd.Stdin = os.Stdin buildCmd.Stdout = os.Stdout buildCmd.Stderr = os.Stderr - buildCmd.Run() - // cleanup - if err := os.RemoveAll(pkg); err != nil { - fmt.Fprintf(os.Stderr, "warning: failed to remove %s: %v\n", pkg, err) + if err := buildCmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "makepkg failed: %v\n", err) + os.Exit(1) } fmt.Println("install successful") }, } +func copyDir(src, dst string) error { + if err := os.MkdirAll(dst, 0755); err != nil { + return err + } + return filepath.Walk(src, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + rel, err := filepath.Rel(src, path) + if err != nil { + return err + } + target := filepath.Join(dst, rel) + if info.IsDir() { + return os.MkdirAll(target, info.Mode()) + } + in, err := os.Open(path) + if err != nil { + return err + } + defer in.Close() + out, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, info.Mode()) + if err != nil { + return err + } + defer out.Close() + _, err = io.Copy(out, in) + return err + }) +} + func init() { rootCmd.AddCommand(installCmd) }