1220 lines
40 KiB
Go
1220 lines
40 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"eon/common"
|
|
"eon/lib"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"regexp"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"net/http"
|
|
"net/url"
|
|
|
|
"golang.org/x/sys/unix"
|
|
"golang.org/x/term"
|
|
|
|
"github.com/dustin/go-humanize"
|
|
"github.com/fatih/color"
|
|
)
|
|
|
|
func main() {
|
|
if len(os.Args) < 2 {
|
|
fmt.Println("Usage: eon <list/info/install/remove/clean/repo/help> [args]")
|
|
os.Exit(1)
|
|
}
|
|
|
|
logger := &common.DefaultLogger
|
|
|
|
for i, arg := range os.Args {
|
|
if arg == "--help" || arg == "-h" || (i != 1 && arg == "help") {
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "To ask for help, use 'eon help <list/info/install/remove/clean/repo/help>', not --help, -h, or eon <command> help.",
|
|
})
|
|
}
|
|
}
|
|
switch os.Args[1] {
|
|
case "help":
|
|
if len(os.Args) < 3 {
|
|
fmt.Println("Usage: eon help (list/info/install/remove/clean/repo/help) [args]")
|
|
os.Exit(1)
|
|
} else {
|
|
switch os.Args[2] {
|
|
case "list":
|
|
fmt.Println("Usage: eon list (repo/local)")
|
|
fmt.Println("- repo: lists packages available in repositories.")
|
|
fmt.Println("- local: lists installed packages.")
|
|
case "info":
|
|
fmt.Println("Usage: eon info <package> [(repo/local)] [<repository>]")
|
|
fmt.Println("Shows information about a package.")
|
|
case "install":
|
|
fmt.Println("Usage: eon install <package> [<repository>]")
|
|
fmt.Println("Installs a package.")
|
|
case "remove":
|
|
fmt.Println("Usage: eon remove <package>")
|
|
fmt.Println("Removes a package.")
|
|
case "clean":
|
|
fmt.Println("Usage: eon clean")
|
|
fmt.Println("Removes unused dependencies.")
|
|
case "repo":
|
|
fmt.Println("Usage: eon repo (add/del/list)")
|
|
fmt.Println("- add <packageUrl>: adds a repository.")
|
|
fmt.Println("- del <name>: removes a repository.")
|
|
fmt.Println("- list: lists repositories.")
|
|
case "help":
|
|
fmt.Println("Usage: eon help (list/info/install/remove/clean/repo/help) [args]")
|
|
fmt.Println("Shows help.")
|
|
default:
|
|
fmt.Println(color.RedString("Unknown command: " + os.Args[2]))
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
case "install":
|
|
var forceMode bool
|
|
var yesMode bool
|
|
var inMemoryMode bool
|
|
if len(os.Args) < 3 {
|
|
fmt.Println("Usage: eon install <package>")
|
|
os.Exit(1)
|
|
}
|
|
var localPackageList []string
|
|
fileInfos := make(map[string]os.FileInfo)
|
|
var repoPackageList []string
|
|
for _, pkg := range os.Args[2:] {
|
|
if !strings.HasPrefix(pkg, "-") {
|
|
fileInfo, err := os.Stat(pkg)
|
|
if err != nil || fileInfo.IsDir() {
|
|
if os.IsNotExist(err) || fileInfo.IsDir() {
|
|
// This unholy regex is a package name validator. It validates repository-name/package-name/1.0.0-prerelease.1+meta.1
|
|
// The regex is so unholy it breaks JetBrains' regex parser, so I had to disable it.
|
|
//goland:noinspection RegExpUnnecessaryNonCapturingGroup
|
|
match, err := regexp.Match(`^(?:([a-z0-9_-]+)/)?([a-z0-9_-]+)(?:/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)?$`, []byte(pkg))
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to validate package name: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
if !match {
|
|
fmt.Println(color.RedString("Invalid package name: " + pkg))
|
|
os.Exit(1)
|
|
} else {
|
|
repoPackageList = append(repoPackageList, pkg)
|
|
}
|
|
} else {
|
|
fmt.Println(color.RedString("Failed to get file information: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
} else {
|
|
localPackageList = append(localPackageList, pkg)
|
|
fileInfos[pkg] = fileInfo
|
|
}
|
|
} else {
|
|
switch pkg {
|
|
case "--force", "-f":
|
|
forceMode = true
|
|
case "--optimizeForSpeed", "-O":
|
|
inMemoryMode = true
|
|
case "--yes", "-y":
|
|
yesMode = true
|
|
}
|
|
}
|
|
}
|
|
// We contact the database before we print anything as to not break the formatting.
|
|
err := common.EstablishDBConnection(logger)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to establish a connection to the database: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
// We refresh the package list. This takes a lot of arguments, haha.
|
|
err = common.RefreshPackageList(common.DefaultListRepositoriesInDB, common.DefaultAddRemotePackageToDB,
|
|
common.DefaultGetFingerprintFromDB, common.DefaultAddFingerprintToDB, common.DefaultCheckRepositoryInDB,
|
|
common.DefaultAddRepositoryToDB, common.DefaultListRemotePackagesInDB, common.DefaultRemoveRemotePackageFromDB,
|
|
logger)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to refresh package list: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
// Let's start printing things.
|
|
if len(localPackageList) > 0 || len(repoPackageList) > 0 {
|
|
var epkList []common.InstallPackage
|
|
var skipEpkList [][]string
|
|
var dependencies int
|
|
|
|
localPackageListIteration:
|
|
for _, pkg := range localPackageList {
|
|
// Check if the package is already in epkList.
|
|
for _, epk := range epkList {
|
|
if epk.EPKPreMap.DisplayData.Name == pkg {
|
|
continue localPackageListIteration
|
|
}
|
|
}
|
|
|
|
epkFile, err := os.Open(pkg)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to open package: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
var preMap lib.EPKPreMap
|
|
var vagueErr error
|
|
var epkBytes bytes.Buffer
|
|
if !inMemoryMode {
|
|
preMap, err, vagueErr = lib.PreMapEPK(lib.StreamOrBytes{FileStream: epkFile, IsFileStream: true}, uint64(fileInfos[pkg].Size()))
|
|
} else {
|
|
_, err = io.Copy(bufio.NewWriter(&epkBytes), epkFile)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to read package into memory: " + err.Error() +
|
|
", have you tried disabling in-memory mode?"))
|
|
os.Exit(1)
|
|
}
|
|
err = epkFile.Close()
|
|
if err != nil {
|
|
fmt.Println(color.HiYellowString("Failed to close package file: " + err.Error() + ", memory leak possible."))
|
|
}
|
|
preMap, err, vagueErr = lib.PreMapEPK(lib.StreamOrBytes{Bytes: epkBytes.Bytes()}, uint64(fileInfos[pkg].Size()))
|
|
}
|
|
if err != nil || vagueErr != nil {
|
|
common.PreMapEPKHandleError(err, vagueErr, logger)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Check if the architecture is supported.
|
|
var uts unix.Utsname
|
|
err = unix.Uname(&uts)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to get system architecture: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Check if the architecture is supported.
|
|
if preMap.DisplayData.Architecture != strings.TrimRight(string(uts.Machine[:]), "\x00") {
|
|
if preMap.DisplayData.Architecture != "noarch" {
|
|
fmt.Println(color.RedString("Package architecture not supported: " + preMap.DisplayData.Architecture))
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
preMap.DisplayData.IsDependency = false
|
|
|
|
var installPackage common.InstallPackage
|
|
installPackage.EPKPreMap = &preMap
|
|
installPackage.Url = ""
|
|
installPackage.Priority = 0
|
|
installPackage.IsRemote = false
|
|
installPackage.IsForced = false
|
|
if !inMemoryMode {
|
|
installPackage.StreamOrBytes = lib.StreamOrBytes{
|
|
FileStream: epkFile,
|
|
IsFileStream: true,
|
|
}
|
|
} else {
|
|
installPackage.StreamOrBytes = lib.StreamOrBytes{
|
|
Bytes: epkBytes.Bytes(),
|
|
}
|
|
}
|
|
|
|
installPackage.Repository = lib.Repository{Name: "Local file"}
|
|
preMap.IsUpgrade = true
|
|
|
|
version, exists, err := common.DefaultCheckEPKInDB(preMap.DisplayData.Name)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to check for package in database: " + err.Error()))
|
|
}
|
|
|
|
if exists {
|
|
if version.Compare(&preMap.DisplayData.Version) != -1 {
|
|
if !forceMode {
|
|
skipEpk := []string{preMap.DisplayData.Name, preMap.DisplayData.Architecture, preMap.DisplayData.Version.String(), "Local file", humanize.Bytes(preMap.DisplayData.DecompressedSize), "Skip"}
|
|
skipEpkList = append(skipEpkList, skipEpk)
|
|
} else {
|
|
installPackage.IsForced = true
|
|
var emptyList []string
|
|
var addedDeps int
|
|
epkList, addedDeps, err = common.HandleDependencies(dependencies, installPackage, 0, []lib.RemoteEPK{}, common.DefaultListRemotePackagesInDB, common.DefaultListEPKsInDB, epkList, &emptyList, logger)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to handle dependencies: " + err.Error()))
|
|
os.Exit(1)
|
|
} else {
|
|
dependencies += addedDeps
|
|
}
|
|
epkList = append(epkList, installPackage)
|
|
}
|
|
} else {
|
|
var emptyList []string
|
|
var addedDeps int
|
|
epkList, addedDeps, err = common.HandleDependencies(dependencies, installPackage, 0, []lib.RemoteEPK{}, common.DefaultListRemotePackagesInDB, common.DefaultListEPKsInDB, epkList, &emptyList, logger)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to handle dependencies: " + err.Error()))
|
|
os.Exit(1)
|
|
} else {
|
|
dependencies += addedDeps
|
|
}
|
|
epkList = append(epkList, installPackage)
|
|
}
|
|
} else {
|
|
preMap.IsUpgrade = false
|
|
var emptyList []string
|
|
var addedDeps int
|
|
epkList, addedDeps, err = common.HandleDependencies(dependencies, installPackage, 0, []lib.RemoteEPK{}, common.DefaultListRemotePackagesInDB, common.DefaultListEPKsInDB, epkList, &emptyList, logger)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to handle dependencies: " + err.Error()))
|
|
os.Exit(1)
|
|
} else {
|
|
dependencies += addedDeps
|
|
}
|
|
epkList = append(epkList, installPackage)
|
|
}
|
|
}
|
|
|
|
repoPackageListIteration:
|
|
for _, pkg := range repoPackageList {
|
|
// Check if the package is already in epkList.
|
|
for _, epk := range epkList {
|
|
if epk.EPKPreMap.DisplayData.Name == pkg {
|
|
continue repoPackageListIteration
|
|
}
|
|
}
|
|
|
|
// Check if the package is already installed.
|
|
version, exists, err := common.DefaultCheckEPKInDB(pkg)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to check for package in database: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
var remoteEPK lib.RemoteEPK
|
|
var epkEntry common.InstallPackage
|
|
|
|
remoteEpkList, err := common.DefaultListRemotePackagesInDB()
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to list remote packages: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
epkExists := false
|
|
for _, epk := range remoteEpkList {
|
|
if epk.Name == pkg {
|
|
remoteEPK = epk
|
|
epkExists = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !epkExists {
|
|
fmt.Println(color.RedString("Package not found: " + pkg))
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Calculate the download URL
|
|
epkDownloadUrl, err := url.JoinPath(remoteEPK.Repository.URL, remoteEPK.Path)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to join URL: " + err.Error()))
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
// Pre-map the EPK
|
|
preMap, err, vagueErr := lib.PreMapRemoteEPK(remoteEPK, logger)
|
|
if err != nil || vagueErr != nil {
|
|
common.PreMapRemoteEPKHandleError(err, vagueErr, logger)
|
|
os.Exit(1)
|
|
}
|
|
|
|
preMap.DisplayData.IsDependency = false
|
|
|
|
// Map the data we have
|
|
epkEntry.Priority = 0
|
|
epkEntry.EPKPreMap = &preMap
|
|
epkEntry.Url = epkDownloadUrl
|
|
epkEntry.IsRemote = true
|
|
epkEntry.Repository = remoteEPK.Repository
|
|
if !inMemoryMode {
|
|
epkEntry.StreamOrBytes.IsURL = true
|
|
epkEntry.StreamOrBytes.URL = epkDownloadUrl
|
|
epkEntry.StreamOrBytes.RepositoryName = remoteEPK.Repository.Name
|
|
} else {
|
|
// Download the entire EPK into memory
|
|
epkBytes, err := http.Get(epkDownloadUrl)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to download package: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
if epkBytes.StatusCode != 200 {
|
|
fmt.Println(color.RedString("Failed to download package: " + epkBytes.Status))
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Calculate the total size of the package
|
|
contentLength, err := strconv.ParseUint(epkBytes.Header.Get("Content-Length"), 10, 64)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to get content length: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Print that we are downloading the package
|
|
fmt.Println("\nDownloading package: " + preMap.DisplayData.Name + " (" + humanize.Bytes(contentLength) + ")")
|
|
|
|
// Hide the cursor for reasons explained below.
|
|
fmt.Print("\033[?25l")
|
|
|
|
// Copy the stream into a buffer
|
|
var buffer bytes.Buffer
|
|
_, err = io.Copy(&lib.ProgressWriter{
|
|
Logger: logger,
|
|
Total: contentLength,
|
|
Writer: &buffer,
|
|
}, epkBytes.Body)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to read package into memory: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Set the progress to 100%
|
|
logger.LogFunc(lib.Log{
|
|
Level: "PROGRESS",
|
|
Progress: 1,
|
|
Total: 1,
|
|
})
|
|
|
|
// Show the cursor again because we're done with the progress bar.
|
|
fmt.Print("\033[?25h")
|
|
|
|
// Close the response body
|
|
err = epkBytes.Body.Close()
|
|
if err != nil {
|
|
fmt.Println(color.HiYellowString("Failed to close response body: " + err.Error() + ", memory leak possible."))
|
|
}
|
|
|
|
// Set the buffer as the bytes
|
|
epkEntry.StreamOrBytes.Bytes = buffer.Bytes()
|
|
epkEntry.StreamOrBytes.IsURL = false
|
|
epkEntry.StreamOrBytes.IsFileStream = false
|
|
|
|
// Reset the buffer
|
|
buffer.Reset()
|
|
}
|
|
|
|
// Make decisions on what happens if the package is already installed.
|
|
if exists {
|
|
if version.Compare(&preMap.DisplayData.Version) != -1 {
|
|
// If the version is the same or newer, skip the package.
|
|
if !forceMode {
|
|
skipEpkList = append(skipEpkList, []string{preMap.DisplayData.Name, preMap.DisplayData.Architecture, preMap.DisplayData.Version.String(), remoteEPK.Repository.Name, humanize.Bytes(preMap.DisplayData.DecompressedSize), "Skip"})
|
|
} else {
|
|
// If the version is the same or newer, but the user wants to force the installation, install it.
|
|
epkEntry.IsForced = true
|
|
// We can let it use our remoteEPKList to save a SQL query.
|
|
var emptyList []string
|
|
var addedDeps int
|
|
epkList, addedDeps, err = common.HandleDependencies(dependencies, epkEntry, 0, remoteEpkList, common.DefaultListRemotePackagesInDB, common.DefaultListEPKsInDB, epkList, &emptyList, logger)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to handle dependencies: " + err.Error()))
|
|
os.Exit(1)
|
|
} else {
|
|
// We add the dependencies to the total count.
|
|
dependencies += addedDeps
|
|
}
|
|
epkList = append(epkList, epkEntry)
|
|
}
|
|
} else {
|
|
// If the version is older, install it.
|
|
var emptyList []string
|
|
var addedDeps int
|
|
epkList, addedDeps, err = common.HandleDependencies(dependencies, epkEntry, 0, remoteEpkList, common.DefaultListRemotePackagesInDB, common.DefaultListEPKsInDB, epkList, &emptyList, logger)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to handle dependencies: " + err.Error()))
|
|
os.Exit(1)
|
|
} else {
|
|
dependencies += addedDeps
|
|
}
|
|
epkList = append(epkList, epkEntry)
|
|
}
|
|
} else {
|
|
// If the package is not installed, install it.
|
|
var emptyList []string
|
|
var addedDeps int
|
|
epkList, addedDeps, err = common.HandleDependencies(dependencies, epkEntry, 0, remoteEpkList, common.DefaultListRemotePackagesInDB, common.DefaultListEPKsInDB, epkList, &emptyList, logger)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to handle dependencies: " + err.Error()))
|
|
os.Exit(1)
|
|
} else {
|
|
dependencies += addedDeps
|
|
}
|
|
epkList = append(epkList, epkEntry)
|
|
}
|
|
}
|
|
|
|
// Sort the list of packages to install by priority, then alphabetically.
|
|
sort.Slice(epkList, func(i, j int) bool {
|
|
if epkList[i].Priority == epkList[j].Priority {
|
|
return epkList[i].EPKPreMap.DisplayData.Name < epkList[j].EPKPreMap.DisplayData.Name
|
|
}
|
|
return epkList[i].Priority > epkList[j].Priority
|
|
})
|
|
|
|
// Give the summary of the installation.
|
|
fmt.Println("\nThe following packages will be installed:")
|
|
width, _, err := term.GetSize(int(os.Stdout.Fd()))
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to get terminal width: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
if width < 42 {
|
|
fmt.Println(color.RedString("Terminal too small. Minimum required width: 42 characters."))
|
|
os.Exit(1)
|
|
}
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
fmt.Println()
|
|
tableList := []string{"Package", "Architecture", "Version", "Repository", "Installed Size", "Action"}
|
|
maxSize := width / 6
|
|
for _, item := range tableList {
|
|
common.PrintWithEvenPadding(item, maxSize)
|
|
}
|
|
fmt.Println()
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
fmt.Println()
|
|
|
|
for _, pkg := range skipEpkList {
|
|
for _, item := range pkg {
|
|
common.PrintWithEvenPadding(color.GreenString(item), maxSize)
|
|
}
|
|
}
|
|
|
|
for _, pkg := range epkList {
|
|
finalisedList := make([]string, 6)
|
|
finalisedList[0] = pkg.EPKPreMap.DisplayData.Name
|
|
finalisedList[1] = pkg.EPKPreMap.DisplayData.Architecture
|
|
finalisedList[2] = pkg.EPKPreMap.DisplayData.Version.String()
|
|
if !pkg.IsRemote {
|
|
finalisedList[3] = "Local file"
|
|
} else {
|
|
finalisedList[3] = pkg.Repository.Name
|
|
}
|
|
finalisedList[4] = humanize.Bytes(pkg.EPKPreMap.DisplayData.DecompressedSize)
|
|
if pkg.IsForced {
|
|
finalisedList[5] = "Forced installation"
|
|
} else if pkg.EPKPreMap.IsUpgrade {
|
|
finalisedList[5] = "Upgrade"
|
|
} else {
|
|
finalisedList[5] = "Install"
|
|
}
|
|
for _, item := range finalisedList {
|
|
common.PrintWithEvenPadding(item, maxSize)
|
|
}
|
|
}
|
|
|
|
if len(epkList) > 0 {
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
fmt.Println("Transaction Summary")
|
|
fmt.Println("\nInstalling " + humanize.Comma(int64(len(epkList))) + " packages, of which " + humanize.Comma(int64(dependencies)) + " are dependencies.")
|
|
fmt.Println("Total download size: " + humanize.Bytes(common.GetTotalSize(epkList)))
|
|
fmt.Println("Total installed size: " + humanize.Bytes(common.GetTotalInstalledSize(epkList)) + "\n")
|
|
var response string
|
|
if !yesMode {
|
|
response = logger.LogFunc(lib.Log{
|
|
Level: "INFO",
|
|
Content: "Proceed with installation (y/n)?",
|
|
Prompt: true,
|
|
PlaySound: true,
|
|
})
|
|
}
|
|
if strings.ToLower(response) == "y" || yesMode {
|
|
// We hide the cursor because it makes the progress bar look weird and other package managers hide
|
|
// it during installation. For some reason, it builds suspense. Or it does with me anyway, when
|
|
// a program hides the cursor, it makes me think twice than to Ctrl+C it :P
|
|
// - Arzumify
|
|
fmt.Print("\033[?25l")
|
|
// Time to install things.
|
|
for _, installPackage := range epkList {
|
|
if installPackage.IsRemote {
|
|
if !inMemoryMode {
|
|
// Set the package stream to the URL.
|
|
installPackage.StreamOrBytes.IsURL = true
|
|
installPackage.StreamOrBytes.IsRemote = true
|
|
installPackage.StreamOrBytes.RepositoryName = installPackage.Repository.Name
|
|
installPackage.StreamOrBytes.IsFileStream = false
|
|
installPackage.StreamOrBytes.URL = installPackage.Url
|
|
} else {
|
|
// Download the entire EPK into memory
|
|
epkBytes, err := http.Get(installPackage.Url)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to download package: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
if epkBytes.StatusCode != 200 {
|
|
fmt.Println(color.RedString("Failed to download package: " + epkBytes.Status))
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Start streaming the package into a buffer
|
|
contentLength, err := strconv.ParseUint(epkBytes.Header.Get("Content-Length"), 10, 64)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to get content length: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
fmt.Println("\nDownloading package: " + installPackage.EPKPreMap.DisplayData.Name + " (" + humanize.Bytes(contentLength) + ")")
|
|
var buffer bytes.Buffer
|
|
_, err = io.Copy(&lib.ProgressWriter{
|
|
Logger: logger,
|
|
Total: contentLength,
|
|
Writer: &buffer,
|
|
}, epkBytes.Body)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to read package into memory: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Set the progress to 100%
|
|
logger.LogFunc(lib.Log{
|
|
Level: "PROGRESS",
|
|
Progress: 1,
|
|
Total: 1,
|
|
})
|
|
|
|
// Close the response body
|
|
err = epkBytes.Body.Close()
|
|
if err != nil {
|
|
fmt.Println(color.HiYellowString("Failed to close response body: " + err.Error() + ", memory leak possible."))
|
|
}
|
|
|
|
fmt.Println("Package downloaded")
|
|
|
|
// Set the buffer as the bytes
|
|
installPackage.StreamOrBytes.Bytes = buffer.Bytes()
|
|
installPackage.StreamOrBytes.IsURL = false
|
|
installPackage.StreamOrBytes.IsRemote = true
|
|
installPackage.StreamOrBytes.RepositoryName = installPackage.Repository.Name
|
|
installPackage.StreamOrBytes.IsFileStream = false
|
|
}
|
|
}
|
|
// Map the EPK metadata.
|
|
metadata, err, vagueErr := lib.FullyMapMetadata(installPackage.StreamOrBytes,
|
|
installPackage.EPKPreMap, common.DefaultGetFingerprintFromDB,
|
|
common.DefaultAddFingerprintToDB, func(*lib.Logger) {
|
|
logger.LogFunc(lib.Log{
|
|
Level: "WARN",
|
|
Content: "This server does not support range requests. Please use the -O flag to " +
|
|
"enable in memory mode, or contact the server administrator to use a web server " +
|
|
"that supports range requests, such as Apache or Nginx. The speed of download " +
|
|
"is considerably slowed, as we need to discard bytes since we can only read " +
|
|
"sequentially.",
|
|
Prompt: false,
|
|
})
|
|
}, logger)
|
|
if err != nil || vagueErr != nil {
|
|
common.FullyMapMetadataHandleError(err, vagueErr, logger)
|
|
}
|
|
// Install the package.
|
|
tempDir, err, vagueErr := lib.InstallEPK(installPackage.StreamOrBytes,
|
|
metadata, installPackage.EPKPreMap, common.DefaultAddEPKToDB, logger)
|
|
if err != nil || vagueErr != nil {
|
|
common.InstallEPKHandleError(tempDir, err, vagueErr, logger)
|
|
}
|
|
// Done!
|
|
fmt.Println("Installed package: " + installPackage.EPKPreMap.DisplayData.Name)
|
|
}
|
|
// We show the cursor again because we're done with the progress bar.
|
|
fmt.Print("\033[?25h")
|
|
} else {
|
|
fmt.Println("Installation cancelled.")
|
|
os.Exit(1)
|
|
}
|
|
} else {
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
fmt.Println("No packages left to install. To force re-install / downgrade, use the --force flag, " +
|
|
"though this may result in breakage.")
|
|
os.Exit(0)
|
|
}
|
|
} else {
|
|
fmt.Println("No packages to install.")
|
|
os.Exit(0)
|
|
}
|
|
case "repo":
|
|
if len(os.Args) < 3 {
|
|
fmt.Println("Usage: eon repo (add/del/list)")
|
|
os.Exit(1)
|
|
}
|
|
|
|
switch os.Args[2] {
|
|
case "add":
|
|
if len(os.Args) < 4 {
|
|
fmt.Println("Usage: eon repo add <packageUrl>")
|
|
os.Exit(1)
|
|
} else {
|
|
err := common.EstablishDBConnection(logger)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to establish a connection to the database: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
repoName, err, vagueErr := lib.AddRepository(os.Args[3], common.DefaultAddRepositoryToDB, common.DefaultGetFingerprintFromDB, common.DefaultAddFingerprintToDB, common.DefaultAddRemotePackageToDB, common.DefaultCheckRepositoryInDB, false, logger)
|
|
if err != nil || vagueErr != nil {
|
|
common.AddRepositoryHandleError(err, vagueErr, logger)
|
|
} else {
|
|
fmt.Println("Added repository " + repoName + " to the database.")
|
|
}
|
|
}
|
|
case "del":
|
|
if len(os.Args) < 4 {
|
|
fmt.Println("Usage: eon repo del <name>")
|
|
os.Exit(1)
|
|
} else {
|
|
err := common.EstablishDBConnection(logger)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to establish a connection to the database: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
err, vagueErr := lib.RemoveRepository(os.Args[3], common.DefaultRemoveRepositoryFromDB, common.DefaultCheckRepositoryInDB, logger)
|
|
if err != nil || vagueErr != nil {
|
|
common.RemoveRepositoryHandleError(err, vagueErr, logger)
|
|
}
|
|
}
|
|
case "list":
|
|
err := common.EstablishDBConnection(logger)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to establish a connection to the database: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
repos, err := common.DefaultListRepositoriesInDB()
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to get repositories: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
if len(repos) == 0 {
|
|
fmt.Println("No repositories.")
|
|
} else {
|
|
fmt.Println("Repositories:")
|
|
for _, repo := range repos {
|
|
fmt.Println("\n" + repo.Name + ":\n" + "- " + repo.Description + "\n- URL: " + repo.URL + "\n- Owner: " + repo.Owner)
|
|
}
|
|
fmt.Println()
|
|
}
|
|
}
|
|
case "remove":
|
|
if len(os.Args) < 3 {
|
|
fmt.Println("Usage: eon remove <package>")
|
|
os.Exit(1)
|
|
}
|
|
err := common.EstablishDBConnection(logger)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to establish a connection to the database: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
// Create a map of all installed packages, to minimise the database queries later.
|
|
epkMap, decompressedSizeMap, repositoryMap, _, err := common.DefaultListEPKsInDB()
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to list installed packages: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
// Create a list of packages to remove.
|
|
var packageRemoveList []common.RemovePackage
|
|
for _, pkg := range os.Args[2:] {
|
|
var removePackage common.RemovePackage
|
|
_, exists, err := common.DefaultCheckEPKInDB(pkg)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to check for package in database: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
if !exists {
|
|
fmt.Println(color.RedString("Package not found: " + pkg))
|
|
os.Exit(1)
|
|
}
|
|
// Search to see if it's parent is still installed.
|
|
for _, epk := range epkMap {
|
|
for _, dep := range epk.Dependencies {
|
|
if dep == os.Args[2] {
|
|
// First, check if it's in the list of packages to remove.
|
|
var found bool
|
|
for _, epkName := range os.Args[2:] {
|
|
if epkName == epk.Name {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
fmt.Println(color.RedString("Package " + pkg + " is a dependency of another package (" + epk.Name + ") and cannot be removed."))
|
|
os.Exit(1)
|
|
} else {
|
|
// It is in the list of packages to remove, we need to set this package's priority higher.
|
|
removePackage.Priority++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Add it to the list of packages to remove.
|
|
removePackage.Name = pkg
|
|
removePackage.DisplayData = epkMap[pkg]
|
|
packageRemoveList = append(packageRemoveList, removePackage)
|
|
}
|
|
|
|
if len(packageRemoveList) > 0 {
|
|
// First, re-order the packageRemoveList by priority, then alphabetically.
|
|
sort.Slice(packageRemoveList, func(i, j int) bool {
|
|
if packageRemoveList[i].Priority == packageRemoveList[j].Priority {
|
|
return packageRemoveList[i].Name < packageRemoveList[j].Name
|
|
}
|
|
return packageRemoveList[i].Priority > packageRemoveList[j].Priority
|
|
})
|
|
|
|
// Print the packages that will be removed.
|
|
fmt.Println("The following packages will be removed:")
|
|
width, _, err := term.GetSize(int(os.Stdout.Fd()))
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to get terminal width: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
if width < 42 {
|
|
fmt.Println(color.RedString("Terminal too small. Minimum required width: 42 characters."))
|
|
os.Exit(1)
|
|
}
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
fmt.Println()
|
|
tableList := []string{"Package", "Architecture", "Version", "Repository", "Installed Size", "Action"}
|
|
maxSize := width / 6
|
|
for _, item := range tableList {
|
|
common.PrintWithEvenPadding(item, maxSize)
|
|
}
|
|
fmt.Println()
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
fmt.Println()
|
|
for _, pkg := range packageRemoveList {
|
|
finalisedList := make([]string, 6)
|
|
finalisedList[0] = pkg.Name
|
|
finalisedList[1] = pkg.DisplayData.Architecture
|
|
finalisedList[2] = pkg.DisplayData.Version.String()
|
|
finalisedList[3] = repositoryMap[pkg.Name].Name
|
|
finalisedList[4] = humanize.Bytes(decompressedSizeMap[pkg.Name])
|
|
finalisedList[5] = "Remove"
|
|
for _, item := range finalisedList {
|
|
common.PrintWithEvenPadding(item, maxSize)
|
|
}
|
|
}
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
fmt.Println("Transaction Summary")
|
|
fmt.Println("\nRemoving " + humanize.Comma(int64(len(packageRemoveList))) + " packages.")
|
|
var space uint64
|
|
for _, pkg := range packageRemoveList {
|
|
space += decompressedSizeMap[pkg.Name]
|
|
}
|
|
fmt.Println("Total reclaimed space: " + humanize.Bytes(space) + "\n")
|
|
response := logger.LogFunc(lib.Log{
|
|
Level: "INFO",
|
|
Content: "Proceed with removal (y/n)?",
|
|
Prompt: true,
|
|
})
|
|
if strings.ToLower(response) == "y" {
|
|
for _, pkg := range packageRemoveList {
|
|
err, vagueErr := lib.RemoveEPK(pkg.Name, common.DefaultRemoveEPKFromDB, common.DefaultGetEPKRemoveInfoFromDB, logger)
|
|
if err != nil || vagueErr != nil {
|
|
common.RemoveEPKHandleError(err, vagueErr, logger)
|
|
fmt.Println(color.RedString("Failed to remove package: " + vagueErr.Error() + err.Error()))
|
|
}
|
|
fmt.Println("Removed package: " + pkg.Name)
|
|
}
|
|
} else {
|
|
fmt.Println("Removal cancelled.")
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
case "list":
|
|
if len(os.Args) < 3 {
|
|
fmt.Println("Usage: eon list (remote/local)")
|
|
os.Exit(1)
|
|
}
|
|
|
|
err := common.EstablishDBConnection(logger)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to establish a connection to the database: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
width, _, err := term.GetSize(int(os.Stdout.Fd()))
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to get terminal width: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
if width < 15 {
|
|
fmt.Println(color.RedString("Terminal too small. Minimum required width: 15 characters."))
|
|
os.Exit(1)
|
|
}
|
|
|
|
maxWidth := width / 3
|
|
|
|
switch os.Args[2] {
|
|
case "remote":
|
|
// Refresh the package list.
|
|
err = common.RefreshPackageList(common.DefaultListRepositoriesInDB, common.DefaultAddRemotePackageToDB,
|
|
common.DefaultGetFingerprintFromDB, common.DefaultAddFingerprintToDB, common.DefaultCheckRepositoryInDB,
|
|
common.DefaultAddRepositoryToDB, common.DefaultListRemotePackagesInDB, common.DefaultRemoveRemotePackageFromDB,
|
|
logger)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to refresh package list: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
remoteEpkList, err := common.DefaultListRemotePackagesInDB()
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to list remote packages: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
if len(remoteEpkList) == 0 {
|
|
fmt.Println("No remote packages.")
|
|
} else {
|
|
fmt.Println("\nRemote packages:")
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
for _, item := range []string{"Name", "Version", "Repository"} {
|
|
common.PrintWithEvenPadding(item, maxWidth)
|
|
}
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
for _, epk := range remoteEpkList {
|
|
var printMap []string
|
|
printMap = append(printMap, epk.Name, epk.Version.String(), epk.Repository.Name)
|
|
for _, item := range printMap {
|
|
common.PrintWithEvenPadding(item, maxWidth)
|
|
}
|
|
fmt.Println()
|
|
}
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
}
|
|
case "local":
|
|
epkMap, _, repositoryMap, _, err := common.DefaultListEPKsInDB()
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to list installed packages: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
if len(epkMap) == 0 {
|
|
fmt.Println("No local packages.")
|
|
} else {
|
|
// Sort the map by name.
|
|
var epkList []string
|
|
for epk := range epkMap {
|
|
epkList = append(epkList, epk)
|
|
}
|
|
sort.Strings(epkList)
|
|
fmt.Println("Local packages:")
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
for _, item := range []string{"Name", "Version", "Repository"} {
|
|
common.PrintWithEvenPadding(item, maxWidth)
|
|
}
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
for _, epk := range epkList {
|
|
version := epkMap[epk].Version
|
|
var printMap []string
|
|
printMap = append(printMap, epk, version.String(), repositoryMap[epk].Name)
|
|
for _, item := range printMap {
|
|
common.PrintWithEvenPadding(item, maxWidth)
|
|
}
|
|
fmt.Println()
|
|
}
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
}
|
|
}
|
|
case "info":
|
|
if len(os.Args) < 3 {
|
|
fmt.Println("Usage: eon info <package>")
|
|
os.Exit(1)
|
|
}
|
|
|
|
err := common.EstablishDBConnection(logger)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to establish a connection to the database: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
err = common.RefreshPackageList(common.DefaultListRepositoriesInDB, common.DefaultAddRemotePackageToDB,
|
|
common.DefaultGetFingerprintFromDB, common.DefaultAddFingerprintToDB, common.DefaultCheckRepositoryInDB,
|
|
common.DefaultAddRepositoryToDB, common.DefaultListRemotePackagesInDB, common.DefaultRemoveRemotePackageFromDB,
|
|
logger)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to refresh package list: " + err.Error()))
|
|
}
|
|
|
|
remoteEpks, err := common.DefaultListRemotePackagesInDB()
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to list installed packages: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
var remoteEpk lib.RemoteEPK
|
|
for _, epk := range remoteEpks {
|
|
if epk.Name == os.Args[2] {
|
|
remoteEpk = epk
|
|
break
|
|
}
|
|
}
|
|
|
|
if remoteEpk.Name == "" {
|
|
fmt.Println(color.RedString("Package not found: " + os.Args[2]))
|
|
os.Exit(1)
|
|
}
|
|
|
|
epkPreMap, err, vagueErr := lib.PreMapRemoteEPK(remoteEpk, logger)
|
|
if err != nil || vagueErr != nil {
|
|
common.PreMapRemoteEPKHandleError(err, vagueErr, logger)
|
|
}
|
|
|
|
packageUrl, err := url.JoinPath(remoteEpk.Repository.URL, remoteEpk.Path)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to join URL: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
metadata, err, vagueErr := lib.FullyMapMetadata(lib.StreamOrBytes{
|
|
IsURL: true,
|
|
URL: packageUrl,
|
|
}, &epkPreMap, common.DefaultGetFingerprintFromDB, common.DefaultAddFingerprintToDB, func(*lib.Logger) {
|
|
logger.LogFunc(lib.Log{
|
|
Level: "WARN",
|
|
Content: "This server does not support range requests. Please use the -O flag to " +
|
|
"enable in memory mode, or contact the server administrator to use a web server " +
|
|
"that supports range requests, such as Apache or Nginx. The speed of download " +
|
|
"is considerably slowed, as we need to discard bytes since we can only read " +
|
|
"sequentially.",
|
|
Prompt: false,
|
|
})
|
|
}, logger)
|
|
if err != nil || vagueErr != nil {
|
|
common.FullyMapMetadataHandleError(err, vagueErr, logger)
|
|
}
|
|
|
|
width, _, err := term.GetSize(int(os.Stdout.Fd()))
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to get terminal width: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
if width < 11 {
|
|
fmt.Println(color.RedString("Terminal too small. Minimum required width: 11 characters."))
|
|
os.Exit(1)
|
|
}
|
|
|
|
fmt.Println()
|
|
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
|
|
common.PrintWithEvenPadding("Information", width/2)
|
|
common.PrintWithEvenPadding("Content", width/2)
|
|
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
|
|
common.PrintWithEvenPadding("Name", width/2)
|
|
common.PrintWithEvenPadding(epkPreMap.DisplayData.Name, width/2)
|
|
|
|
common.PrintWithEvenPadding("Version", width/2)
|
|
common.PrintWithEvenPadding(metadata.Version.String(), width/2)
|
|
|
|
common.PrintWithEvenPadding("Description", width/2)
|
|
common.PrintWithEvenPadding(metadata.Description, width/2)
|
|
|
|
common.PrintWithEvenPadding("License", width/2)
|
|
common.PrintWithEvenPadding(metadata.License, width/2)
|
|
|
|
common.PrintWithEvenPadding("Architecture", width/2)
|
|
common.PrintWithEvenPadding(metadata.Architecture, width/2)
|
|
|
|
common.PrintWithEvenPadding("Installed Size", width/2)
|
|
common.PrintWithEvenPadding(humanize.Bytes(metadata.DecompressedSize), width/2)
|
|
|
|
common.PrintWithEvenPadding("Download Size", width/2)
|
|
common.PrintWithEvenPadding(humanize.Bytes(metadata.Size), width/2)
|
|
|
|
common.PrintWithEvenPadding("Repository", width/2)
|
|
common.PrintWithEvenPadding(remoteEpk.Repository.Name, width/2)
|
|
|
|
common.PrintWithEvenPadding("Dependencies", width/2)
|
|
if len(metadata.Dependencies) == 0 {
|
|
common.PrintWithEvenPadding("None", width/2)
|
|
} else {
|
|
common.PrintWithEvenPadding(strings.Join(metadata.Dependencies, ", "), width/2)
|
|
}
|
|
|
|
// We don't print the long description - it's meant for GUIs.
|
|
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
case "clean":
|
|
err := common.EstablishDBConnection(logger)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to establish a connection to the database: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
packages, _, repositoryMap, _, err := common.DefaultListEPKsInDB()
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to list installed packages: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
if len(packages) == 0 {
|
|
fmt.Println("No packages to clean.")
|
|
os.Exit(0)
|
|
} else {
|
|
var removeList []common.RemovePackage
|
|
for _, pkg := range packages {
|
|
// Check if it's a dependency of another package.
|
|
if pkg.IsDependency {
|
|
// Add it to list of candidates for removal.
|
|
var removePackage common.RemovePackage
|
|
removePackage.Name = pkg.Name
|
|
removePackage.DisplayData = pkg
|
|
removeList = append(removeList, removePackage)
|
|
}
|
|
}
|
|
|
|
for _, pkg := range removeList {
|
|
// Check if it's parent is still installed.
|
|
for _, epk := range packages {
|
|
for _, dep := range epk.Dependencies {
|
|
if dep == pkg.Name {
|
|
// First, check if it's in the list of packages to remove.
|
|
var found bool
|
|
for _, epkName := range removeList {
|
|
if epkName.Name == epk.Name {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
fmt.Println(color.RedString("Package " + pkg.Name + " is a dependency of another package (" + epk.Name + ") and cannot be removed."))
|
|
os.Exit(1)
|
|
} else {
|
|
// It is in the list of packages to remove, we need to set this package's priority higher.
|
|
pkg.Priority++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(removeList) > 0 {
|
|
// First, re-order the packageRemoveList by priority, then alphabetically.
|
|
sort.Slice(removeList, func(i, j int) bool {
|
|
if removeList[i].Priority == removeList[j].Priority {
|
|
return removeList[i].Name < removeList[j].Name
|
|
}
|
|
return removeList[i].Priority > removeList[j].Priority
|
|
})
|
|
|
|
// Print the packages that will be removed.
|
|
fmt.Println("The following packages will be removed:")
|
|
width, _, err := term.GetSize(int(os.Stdout.Fd()))
|
|
if err != nil {
|
|
fmt.Println(color.RedString("Failed to get terminal width: " + err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
if width < 42 {
|
|
fmt.Println(color.RedString("Terminal too small. Minimum required width: 42 characters."))
|
|
os.Exit(1)
|
|
}
|
|
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
|
|
tableList := []string{"Package", "Architecture", "Version", "Repository", "Installed Size", "Action"}
|
|
maxSize := width / 6
|
|
for _, item := range tableList {
|
|
common.PrintWithEvenPadding(item, maxSize)
|
|
}
|
|
|
|
fmt.Println()
|
|
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
|
|
for _, pkg := range removeList {
|
|
finalisedList := make([]string, 6)
|
|
finalisedList[0] = pkg.Name
|
|
finalisedList[1] = pkg.DisplayData.Architecture
|
|
finalisedList[2] = pkg.DisplayData.Version.String()
|
|
finalisedList[3] = repositoryMap[pkg.Name].Name
|
|
finalisedList[4] = humanize.Bytes(pkg.DisplayData.DecompressedSize)
|
|
finalisedList[5] = "Remove"
|
|
for _, item := range finalisedList {
|
|
common.PrintWithEvenPadding(item, maxSize)
|
|
}
|
|
fmt.Println()
|
|
}
|
|
|
|
for range width {
|
|
fmt.Print("=")
|
|
}
|
|
|
|
fmt.Println("Transaction Summary")
|
|
fmt.Println("\nRemoving " + humanize.Comma(int64(len(removeList))) + " packages.")
|
|
var space uint64
|
|
for _, pkg := range removeList {
|
|
space += pkg.DisplayData.DecompressedSize
|
|
}
|
|
fmt.Println("Total reclaimed space: " + humanize.Bytes(space) + "\n")
|
|
|
|
response := logger.LogFunc(lib.Log{
|
|
Level: "INFO",
|
|
Content: "Proceed with removal (y/n)?",
|
|
Prompt: true,
|
|
})
|
|
|
|
if strings.ToLower(response) == "y" {
|
|
for _, pkg := range removeList {
|
|
err, vagueErr := lib.RemoveEPK(pkg.Name, common.DefaultRemoveEPKFromDB, common.DefaultGetEPKRemoveInfoFromDB, logger)
|
|
if err != nil || vagueErr != nil {
|
|
common.RemoveEPKHandleError(err, vagueErr, logger)
|
|
fmt.Println(color.RedString("Failed to remove package: " + vagueErr.Error() + err.Error()))
|
|
}
|
|
fmt.Println("Removed package: " + pkg.Name)
|
|
}
|
|
} else {
|
|
fmt.Println("Removal cancelled.")
|
|
os.Exit(1)
|
|
}
|
|
} else {
|
|
fmt.Println("No packages to clean.")
|
|
os.Exit(0)
|
|
}
|
|
}
|
|
default:
|
|
fmt.Println(color.RedString("Unknown or unimplemented command: " + os.Args[1]))
|
|
os.Exit(1)
|
|
}
|
|
}
|