From ab2582dc60657874e6325be5da1edcaf94be9624 Mon Sep 17 00:00:00 2001 From: VetheonGames Date: Thu, 4 Jan 2024 09:49:03 -0700 Subject: [PATCH] Main Update 1 Install.go Changes ``` - Add comments to each file/package to elaborate on their purpose - Add package list functions to install.go to keep track of installed packages and their sources - Remove the dependancy on YAY, we are just gonna handle AUR operations ourselves. - Remove YAY methods from install.go - Add calls to the package install logger to the end of each install functions - Add dependancy install functions to install.go ``` flatpak.go Changes: ``` - Completely write flatpak.go - Include functions for uninstalling flatpak programs - Include functions for updating flatpak programs ``` pacman.go Changes: ``` - Completely write pacman.go - Include functions for uninstalling pacman packages - Include functions for updating pacman packages ``` yay.go Changes: ``` - Completely removed ``` search.go Changes: ``` - Completely write search.go - Include functions for searching pacman - Include functions for searching snap - Include functions for searching flatpak ``` toolcheck.go Changes: ``` - Completely write toolcheck.go - Include functions for checking for Git - Include functions for checking for base-devel group - Include functions for checking for pacman - Include functions for checking for Snapd - Include functions for checking for flatpak - Include function to ask to install flatpak - Include function to ask to install Snapd - Include function to ask to install Git - Include function to ask to install the base-devel group ``` --- cmd/main.go | 3 + pkg/install/install.go | 143 ++++++++++++++++++++++++++++++---- pkg/packagemanager/flatpak.go | 70 +++++++++++++++++ pkg/packagemanager/pacman.go | 33 ++++++++ pkg/packagemanager/snap.go | 3 + pkg/packagemanager/yay.go | 0 pkg/search/search.go | 102 ++++++++++++++++++++++++ pkg/toolcheck/toolcheck.go | 63 +++++++++++++++ 8 files changed, 400 insertions(+), 17 deletions(-) delete mode 100644 pkg/packagemanager/yay.go diff --git a/cmd/main.go b/cmd/main.go index e69de29..68112fd 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -0,0 +1,3 @@ +package main + +// This file is our main entrypoint, and build point for AllPac diff --git a/pkg/install/install.go b/pkg/install/install.go index a98cb62..78daa80 100644 --- a/pkg/install/install.go +++ b/pkg/install/install.go @@ -1,6 +1,11 @@ package install +// This package is responsible for handling our actual install logic. We could have probably gotten away with +// implementing this into the packagemanager package, but this seems like a better way +// because this provides a single interface for all our install functions + import ( + "encoding/json" "os" "os/exec" "os/user" @@ -8,20 +13,86 @@ import ( "fmt" ) +// PackageList represents the mapping of installed packages to their sources +type PackageList map[string]string + +const pkgListFilename = "pkg.list" + +// getPkgListPath returns the file path for the package list +func getPkgListPath() (string, error) { + usr, err := user.Current() + if err != nil { + return "", fmt.Errorf("error getting current user: %v", err) + } + return filepath.Join(usr.HomeDir, ".allpac", pkgListFilename), nil +} + +// readPackageList reads the package list from the file +func readPackageList() (PackageList, error) { + pkgListPath, err := getPkgListPath() + if err != nil { + return nil, err + } + + file, err := os.Open(pkgListPath) + if err != nil { + if os.IsNotExist(err) { + return PackageList{}, nil // Return an empty list if file doesn't exist + } + return nil, fmt.Errorf("error opening package list file: %v", err) + } + defer file.Close() + + var pkgList PackageList + err = json.NewDecoder(file).Decode(&pkgList) + if err != nil { + return nil, fmt.Errorf("error decoding package list: %v", err) + } + + return pkgList, nil +} + +// writePackageList writes the package list to the file +func writePackageList(pkgList PackageList) error { + pkgListPath, err := getPkgListPath() + if err != nil { + return err + } + + file, err := os.Create(pkgListPath) + if err != nil { + return fmt.Errorf("error creating package list file: %v", err) + } + defer file.Close() + + err = json.NewEncoder(file).Encode(pkgList) + if err != nil { + return fmt.Errorf("error encoding package list: %v", err) + } + + return nil +} + +// logInstallation logs the package installation details +func LogInstallation(packageName, source string) error { + pkgList, err := readPackageList() + if err != nil { + return err + } + + pkgList[packageName] = source + + return writePackageList(pkgList) +} + // InstallPackagePacman installs a package using Pacman func InstallPackagePacman(packageName string) error { cmd := exec.Command("sudo", "pacman", "-S", "--noconfirm", packageName) if output, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("error installing package with Pacman: %s, %v", output, err) } - return nil -} - -// InstallPackageYay installs a package using Yay (AUR) -func InstallPackageYay(packageName string) error { - cmd := exec.Command("yay", "-S", "--noconfirm", packageName) - if output, err := cmd.CombinedOutput(); err != nil { - return fmt.Errorf("error installing package with Yay: %s, %v", output, err) + if err := LogInstallation(packageName, "pacman"); err != nil { + return fmt.Errorf("error logging installation: %v", err) } return nil } @@ -32,6 +103,9 @@ func InstallPackageSnap(packageName string) error { if output, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("error installing package with Snap: %s, %v", output, err) } + if err := LogInstallation(packageName, "snap"); err != nil { + return fmt.Errorf("error logging installation: %v", err) + } return nil } @@ -41,19 +115,14 @@ func InstallPackageFlatpak(packageName string) error { if output, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("error installing package with Flatpak: %s, %v", output, err) } - return nil -} - -// InstallSnap installs Snap manually from the AUR -func InstallSnap() error { - if err := cloneAndInstallFromAUR("https://aur.archlinux.org/snapd.git"); err != nil { - return fmt.Errorf("error installing Snap: %v", err) + if err := LogInstallation(packageName, "flatpak"); err != nil { + return fmt.Errorf("error logging installation: %v", err) } return nil } // cloneAndInstallFromAUR clones the given AUR repository and installs it -func cloneAndInstallFromAUR(repoURL string) error { +func CloneAndInstallFromAUR(repoURL string) error { // Get the current user's home directory usr, err := user.Current() if err != nil { @@ -74,7 +143,7 @@ func cloneAndInstallFromAUR(repoURL string) error { return fmt.Errorf("error cloning AUR repo: %s, %v", output, err) } - // Determine the name of the created directory + // Determine the name of the created directory (and the package name) repoName := filepath.Base(repoURL) repoDir := filepath.Join(baseDir, repoName) @@ -89,5 +158,45 @@ func cloneAndInstallFromAUR(repoURL string) error { return fmt.Errorf("error building package with makepkg: %s, %v", output, err) } + // Log the installation + if err := LogInstallation(repoName, "aur"); err != nil { + return fmt.Errorf("error logging installation: %v", err) + } + + return nil +} + +// InstallSnap installs Snap manually from the AUR +func InstallSnap() error { + if err := CloneAndInstallFromAUR("https://aur.archlinux.org/snapd.git"); err != nil { + return fmt.Errorf("error installing Snap: %v", err) + } + if err := LogInstallation("snapd", "aur"); err != nil { + return fmt.Errorf("error logging installation: %v", err) + } + return nil +} + +// InstallGit installs Git using Pacman +func InstallGit() error { + if err := InstallPackagePacman("git"); err != nil { + return fmt.Errorf("error installing Git: %v", err) + } + return nil +} + +// InstallBaseDevel installs the base-devel group using Pacman +func InstallBaseDevel() error { + if err := InstallPackagePacman("base-devel"); err != nil { + return fmt.Errorf("error installing base-devel: %v", err) + } + return nil +} + +// InstallFlatpak installs the Flatpak package using Pacman +func InstallFlatpak() error { + if err := InstallPackagePacman("flatpak"); err != nil { + return fmt.Errorf("error installing flatpak: %v", err) + } return nil } diff --git a/pkg/packagemanager/flatpak.go b/pkg/packagemanager/flatpak.go index e69de29..631beeb 100644 --- a/pkg/packagemanager/flatpak.go +++ b/pkg/packagemanager/flatpak.go @@ -0,0 +1,70 @@ +package packagemanager + +// This package is responsible for handling updating and uninstalling flatpak applications + +import ( + "os/exec" + "encoding/json" + "io/ioutil" + "fmt" + "path/filepath" + "os/user" +) + +// PackageList represents the mapping of installed packages to their sources +type PackageList map[string]string + +// getPkgListPath returns the file path for the package list +func getPkgListPath() (string, error) { + usr, err := user.Current() + if err != nil { + return "", fmt.Errorf("error getting current user: %v", err) + } + return filepath.Join(usr.HomeDir, ".allpac", "pkg.list"), nil +} + +// readPackageList reads the package list from the file +func readPackageList() (PackageList, error) { + pkgListPath, err := getPkgListPath() + if err != nil { + return nil, err + } + + file, err := ioutil.ReadFile(pkgListPath) + if err != nil { + return nil, fmt.Errorf("error reading package list file: %v", err) + } + + var pkgList PackageList + err = json.Unmarshal(file, &pkgList) + if err != nil { + return nil, fmt.Errorf("error decoding package list: %v", err) + } + + return pkgList, nil +} + +// UpdateFlatpakPackages updates specified Flatpak packages or all if no specific package is provided +func UpdateFlatpakPackages(packageNames ...string) error { + var cmd *exec.Cmd + if len(packageNames) == 0 { + cmd = exec.Command("flatpak", "update", "-y") + } else { + args := append([]string{"update", "-y"}, packageNames...) + cmd = exec.Command("flatpak", args...) + } + + if output, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("error updating Flatpak packages: %s, %v", output, err) + } + return nil +} + +// UninstallFlatpakPackage uninstalls a specified Flatpak package +func UninstallFlatpakPackage(packageName string) error { + cmd := exec.Command("flatpak", "uninstall", "-y", packageName) + if output, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("error uninstalling Flatpak package: %s, %v", output, err) + } + return nil +} diff --git a/pkg/packagemanager/pacman.go b/pkg/packagemanager/pacman.go index e69de29..06cfa88 100644 --- a/pkg/packagemanager/pacman.go +++ b/pkg/packagemanager/pacman.go @@ -0,0 +1,33 @@ +package packagemanager + +// This package is responsible for handling updating and uninstalling pacman packages + +import ( + "os/exec" + "fmt" +) + +// UpdatePacmanPackages updates specified Pacman packages or all if no specific package is provided +func UpdatePacmanPackages(packageNames ...string) error { + var cmd *exec.Cmd + if len(packageNames) == 0 { + cmd = exec.Command("sudo", "pacman", "-Syu") + } else { + args := append([]string{"-S", "--noconfirm"}, packageNames...) + cmd = exec.Command("sudo", "pacman", args...) + } + + if output, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("error updating Pacman packages: %s, %v", output, err) + } + return nil +} + +// UninstallPacmanPackage uninstalls a specified Pacman package +func UninstallPacmanPackage(packageName string) error { + cmd := exec.Command("sudo", "pacman", "-Rns", "--noconfirm", packageName) + if output, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("error uninstalling Pacman package: %s, %v", output, err) + } + return nil +} diff --git a/pkg/packagemanager/snap.go b/pkg/packagemanager/snap.go index e69de29..19b95ee 100644 --- a/pkg/packagemanager/snap.go +++ b/pkg/packagemanager/snap.go @@ -0,0 +1,3 @@ +package packagemanager + +// This package is responsible for handling updating and uninstalling snapd applications diff --git a/pkg/packagemanager/yay.go b/pkg/packagemanager/yay.go deleted file mode 100644 index e69de29..0000000 diff --git a/pkg/search/search.go b/pkg/search/search.go index e69de29..f59e170 100644 --- a/pkg/search/search.go +++ b/pkg/search/search.go @@ -0,0 +1,102 @@ +package search + +// This package is responsible for searching various sources for the availability of the requested package + +import ( + "os/exec" + "encoding/json" + "fmt" + "net/http" + "strings" +) + +// AURResponse represents the structure of the response from AUR RPC +type AURResponse struct { + Version int `json:"version"` + Type string `json:"type"` + ResultCount int `json:"resultcount"` + Results []AURPackage `json:"results"` +} + +// AURPackage represents a package in the AUR +type AURPackage struct { + ID int `json:"ID"` + Name string `json:"Name"` + Version string `json:"Version"` + Description string `json:"Description"` + URL string `json:"URL"` + // Add other fields as needed +} + +// SearchPacman searches for a package in the Pacman repositories +func SearchPacman(packageName string) ([]string, error) { + cmd := exec.Command("pacman", "-Ss", packageName) + output, err := cmd.CombinedOutput() + if err != nil { + return nil, fmt.Errorf("error searching Pacman: %v", err) + } + return parsePacmanOutput(string(output)), nil +} + +// SearchSnap searches for a package in the Snap store +func SearchSnap(packageName string) ([]string, error) { + cmd := exec.Command("snap", "find", packageName) + output, err := cmd.CombinedOutput() + if err != nil { + return nil, fmt.Errorf("error searching Snap: %v", err) + } + return strings.Split(string(output), "\n"), nil +} + +// SearchFlatpak searches for a package in Flatpak repositories +func SearchFlatpak(packageName string) ([]string, error) { + cmd := exec.Command("flatpak", "search", packageName) + output, err := cmd.CombinedOutput() + if err != nil { + return nil, fmt.Errorf("error searching Flatpak: %v", err) + } + return strings.Split(string(output), "\n"), nil +} + +// SearchAUR searches the AUR for the given term +func SearchAUR(searchTerm string) ([]AURPackage, error) { + url := fmt.Sprintf("https://aur.archlinux.org/rpc/?v=5&type=search&arg=%s", searchTerm) + resp, err := http.Get(url) + if err != nil { + return nil, fmt.Errorf("error making request to AUR: %v", err) + } + defer resp.Body.Close() + + var aurResponse AURResponse + if err := json.NewDecoder(resp.Body).Decode(&aurResponse); err != nil { + return nil, fmt.Errorf("error decoding AUR response: %v", err) + } + + return aurResponse.Results, nil +} + +// parsePacmanOutput parses the output from Pacman search command +func parsePacmanOutput(output string) []string { + // Split the output into sections, each representing a package + sections := strings.Split(output, "\n\n") + + var packages []string + for _, section := range sections { + // Split each section into lines + lines := strings.Split(section, "\n") + + // The first line should contain the package name and version + if len(lines) > 0 { + packageNameLine := lines[0] + + // Check if the package is installed + if strings.Contains(packageNameLine, "[installed]") { + packageNameLine += " (Installed)" + } + + packages = append(packages, packageNameLine) + } + } + + return packages +} diff --git a/pkg/toolcheck/toolcheck.go b/pkg/toolcheck/toolcheck.go index e69de29..582d325 100644 --- a/pkg/toolcheck/toolcheck.go +++ b/pkg/toolcheck/toolcheck.go @@ -0,0 +1,63 @@ +package toolcheck + +// This package is responsible for checking to ensure all our tools are available to us. +// Since we aren't hooking directly into the internals of anything, we require the availability of the packages +// on the system in order to use their CLIs. +// In the future, we might hook directly into the backends for pacman, flatpak, and snapd +// but for now, this is a perfectly fine way of going about it without introducing weird bugs + +import ( + "os/exec" + "fmt" + "pixelridgesoftworks.com/AllPac/pkg/install" +) + +// isCommandAvailable checks if a command exists +func isCommandAvailable(name string) bool { + cmd := exec.Command("which", name) + if err := cmd.Run(); err != nil { + return false + } + return true +} + +// EnsurePacman ensures that Pacman is installed and available +func EnsurePacman() error { + if !isCommandAvailable("pacman") { + // Pacman should always be available on Arch-based systems, handle this as an error or special case + return fmt.Errorf("pacman is not available, which is required for AllPac to function") + } + return nil +} + +// EnsureSnap ensures that Snap is installed and available +func EnsureSnap() error { + if !isCommandAvailable("snap") { + return install.InstallSnap() + } + return nil +} + +// EnsureGit ensures that Git is installed and available +func EnsureGit() error { + if !isCommandAvailable("git") { + return install.InstallGit() + } + return nil +} + +// EnsureBaseDevel ensures that the base-devel group is installed +func EnsureBaseDevel() error { + if !isCommandAvailable("make") { // 'make' is part of base-devel, this is the best method to check + return install.InstallBaseDevel() + } + return nil +} + +// EnsureFlatpak ensures that Flatpak is installed and available +func EnsureFlatpak() error { + if !isCommandAvailable("flatpak") { + return install.InstallFlatpak() + } + return nil +}