Skip to content

Commit

Permalink
Implements install, updating, uninstall and more
Browse files Browse the repository at this point in the history
TODO Update commit once done.
  • Loading branch information
Bios-Marcel committed Apr 11, 2024
1 parent ab52cef commit a913d81
Show file tree
Hide file tree
Showing 27 changed files with 1,806 additions and 360 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
*.exe
!/pkg/scoop/shim.exe
/spoon
*.pprof

47 changes: 27 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ relying on the existing community work in form of buckets.

* More thorough `scoop search`
* Better performance (Varies from command to command)
* Behaviour changes
* `spoon install app@version` will now use an old manifest and hold the app, instead
of generating a manifest (destined to be buggy)
* Additional features
* Tab completion for commands, flags and packages
* Common command aliases
Expand All @@ -21,8 +24,13 @@ below.

## Breaking Changes

* No automatic `spoon update` calls during `install`, `download`, ...
* The `--global` flag hasn't beren implemented anywhere and I am not planning to
do so as of now. If there's demand in the future, I will consider.
* Only `kiennq/shim.exe` is supported for shimming
> The older shim formats were included in scoop for backwards compatibility
> reasons. The solution is probably to simply reinstall all currently
> installed packages via `scoop export` and `scoop import`.
## Manual Installation

Expand All @@ -41,6 +49,16 @@ below.
Note that self-updating is *NOT YET* possible. To update, please use `scoop
update spoon` for now.

## Runtime dependencies

While spoon is written in Golang, it has runtime dependencies needed for
installing. Rewriting those would provide little to no value and cost a lot of
value.

* [shim.exe](https://github.com/kiennq/scoop-better-shimexe) - Included in
Binary - MIT/Unlicense
* ... TODO

## CLI Progress

Progress overview for scoop command implementations. This does NOT include spoon
Expand All @@ -60,24 +78,24 @@ There are basically three levels of implementations (and the states inbetween):
| ---------- | ------------------- | ------------------------------------------------------------------------ |
| help | Native | |
| search | Native | * Performance improvements<br/>* JSON output<br/> * Search configuration |
| install | Wrapper | |
| uninstall | Wrapper | * Terminate running processes |
| update | Partially Native | * Now invokes `status` after updating buckets |
| bucket | Partially Native | * `bucket rm` now supports multiple buckets to delete at once |
| download | Native | * Support for multiple apps to download at once |
| cat | Native | * Alias `manifest`<br/>* Allow getting specific manifest versions |
| status | Native | * `--local` has been deleted (It's always local now)<br/>* Shows outdated / installed things scoop didn't (due to bugs) |
| info | Wrapper | |
| depends | Native (WIP) | * Adds `--reverse/-r` flag<br/>* Prints an ASCII tree by default |
| update | Partially Native | * Now invokes `status` after updating buckets |
| bucket | Partially Native | * `bucket rm` now supports multiple buckets to delete at once |
| install | Native (WIP) | * Installing a specific version doesn't generate manifests anymore, but uses an old existing manifest and sets the installed app to `held`. |
| uninstall | Native (WIP) | * Terminate running processes |
| info | Wrapper | |
| shim | Planned Next | |
| unhold | Planned Next | |
| hold | Planned Next | |
| list | | |
| hold | | |
| unhold | | |
| reset | | |
| cleanup | | |
| create | | |
| shim | | |
| which | | |
| config | | |
| download | | |
| cache | | |
| prefix | | |
| home | | |
Expand All @@ -87,14 +105,3 @@ There are basically three levels of implementations (and the states inbetween):
| virustotal | | |
| alias | | |

## Search

The search here does nothing fancy, it simply does an offline search of
buckets, just like what scoop does, but faster. Online search is not supported
as I deem it unnecessary. If you want to search the latest, simply run
`scoop update; spoon search <app>`.

The search command allows plain output and JSON output. This allows use with
tools such as `jq` or direct use in powershell via Powershells builtin
`ConvertFrom-Json`.

14 changes: 10 additions & 4 deletions cmd/spoon/cat.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ func catCmd() *cobra.Command {
return fmt.Errorf("error getting default scoop: %w", err)
}

app, err := defaultScoop.GetAvailableApp(args[0])
app, err := defaultScoop.FindAvailableApp(args[0])
if err != nil {
return fmt.Errorf("error finding app: %w", err)
}

if app == nil {
installedApp, err := defaultScoop.GetInstalledApp(args[0])
installedApp, err := defaultScoop.FindInstalledApp(args[0])
if err != nil {
return fmt.Errorf("error finding app: %w", err)
}
Expand All @@ -44,12 +44,18 @@ func catCmd() *cobra.Command {
app = installedApp.App
}

var reader io.ReadCloser
var reader io.Reader
_, _, version := scoop.ParseAppIdentifier(args[0])
if version != "" {
reader, err = app.ManifestForVersion(version)
} else {
reader, err = os.Open(app.ManifestPath())
fileReader, tempErr := os.Open(app.ManifestPath())
if fileReader != nil {
defer fileReader.Close()
reader = fileReader
} else {
err = tempErr
}
}

if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/spoon/complete.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func autocompleteInstalled(
return nil, cobra.ShellCompDirectiveNoFileComp
}

apps, err := defaultScoop.GetInstalledApps()
apps, err := defaultScoop.InstalledApps()
if err != nil {
return nil, cobra.ShellCompDirectiveNoFileComp
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/spoon/depends.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func dependsCmd() *cobra.Command {
if err != nil {
return fmt.Errorf("error getting default scoop: %w", err)
}
app, err := defaultScoop.GetAvailableApp(args[0])
app, err := defaultScoop.FindAvailableApp(args[0])
if err != nil {
return fmt.Errorf("error looking up app: %w", err)
}
Expand Down
103 changes: 103 additions & 0 deletions cmd/spoon/download.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package main

import (
"errors"
"fmt"
"os"
"path/filepath"

"github.com/Bios-Marcel/spoon/pkg/scoop"
"github.com/spf13/cobra"
)

func downloadCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "download",
Short: "Download all files required for a package",
Args: cobra.MinimumNArgs(1),
ValidArgsFunction: autocompleteAvailable,
RunE: RunE(func(cmd *cobra.Command, args []string) error {
arch := scoop.ArchitectureKey(must(cmd.Flags().GetString("arch")))
force := must(cmd.Flags().GetBool("force"))
noHashCheck := must(cmd.Flags().GetBool("no-hash-check"))

defaultScoop, err := scoop.NewScoop()
if err != nil {
return fmt.Errorf("error retrieving scoop instance: %w", err)
}

for _, arg := range args {
app, err := defaultScoop.FindAvailableApp(arg)
if err != nil {
return fmt.Errorf("error looking up app: %w", err)
}
if app == nil {
return fmt.Errorf("app '%s' not found", arg)
}

if err := app.LoadDetails(
scoop.DetailFieldArchitecture,
scoop.DetailFieldUrl,
scoop.DetailFieldHash,
); err != nil {
return fmt.Errorf("error loading app details: %w", err)
}

resolvedApp := app.ForArch(arch)
resultChan, err := resolvedApp.Download(
defaultScoop.CacheDir(), arch, !noHashCheck, force,
)
if err != nil {
return err
}

for result := range resultChan {
switch result := result.(type) {
case *scoop.CacheHit:
name := filepath.Base(result.Downloadable.URL)
fmt.Printf("Cache hit for '%s'\n", name)
case *scoop.FinishedDownload:
name := filepath.Base(result.Downloadable.URL)
fmt.Printf("Downloaded '%s'\n", name)
case error:
var checksumErr *scoop.ChecksumMismatchError
if errors.As(result, &checksumErr) {
fmt.Printf(
"Checksum mismatch:\n\rFile: '%s'\n\tExpected: '%s'\n\tActual: '%s'\n",
checksumErr.File,
checksumErr.Expected,
checksumErr.Actual,
)

// FIXME Find a better way to do this via
// returnvalue?
os.Exit(1)
}
if result != nil {
return result
}
}
}
}

return nil
}),
}

cmd.Flags().BoolP("force", "f", false, "Force download (overwrite cache)")
// FIXME No shorthand for now, since --h is help and seems to clash.
cmd.Flags().Bool("no-hash-check", false, "Skip hash verification (use with caution!)")
// We default to our system architecture here. If scoop encounters an
// unsupported arch, it is ignored. We'll do the same.
cmd.Flags().StringP("arch", "a", string(SystemArchitecture),
"use specified architecture, if app supports it")
cmd.RegisterFlagCompletionFunc("arch", cobra.FixedCompletions(
[]string{
string(scoop.ArchitectureKey32Bit),
string(scoop.ArchitectureKey64Bit),
string(scoop.ArchitectureKeyARM64),
},
cobra.ShellCompDirectiveDefault))

return cmd
}
33 changes: 20 additions & 13 deletions cmd/spoon/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,30 @@ func installCmd() *cobra.Command {
Short: "Install a package",
Args: cobra.MinimumNArgs(1),
ValidArgsFunction: autocompleteAvailable,
Run: func(cmd *cobra.Command, args []string) {
flags, err := getFlags(cmd, "global", "independent", "no-cache", "no-update-scoop", "skip", "arch")
if err != nil {
fmt.Println(err)
os.Exit(1)
RunE: RunE(func(cmd *cobra.Command, args []string) error {
// Flags we currently do not support
if must(cmd.Flags().GetBool("global")) {
flags, err := getFlags(cmd, "global", "independent", "no-cache",
"no-update-scoop", "skip", "arch")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
os.Exit(execScoopCommand("install", append(flags, args...)...))
}

// Default path, where we can't do our simple optimisation of
// parallelising install and download, as we only have one package.
if len(args) == 1 {
os.Exit(execScoopCommand("install", append(flags, args...)...))
return
arch := must(cmd.Flags().GetString("arch"))

defaultScoop, err := scoop.NewScoop()
if err != nil {
return fmt.Errorf("error retrieving scoop instance: %w", err)
}

// FIXME Parallelise.
os.Exit(execScoopCommand("install", append(flags, args...)...))
},
if err := defaultScoop.InstallAll(args, scoop.ArchitectureKey(arch)); err != nil {
return err
}
return nil
}),
}

cmd.Flags().BoolP("global", "g", false, "Install an app globally")
Expand Down
1 change: 1 addition & 0 deletions cmd/spoon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func main() {
}

rootCmd.AddCommand(searchCmd())
rootCmd.AddCommand(downloadCmd())
rootCmd.AddCommand(installCmd())
rootCmd.AddCommand(uninstallCmd())
rootCmd.AddCommand(updateCmd())
Expand Down
2 changes: 0 additions & 2 deletions cmd/spoon/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import (
"strings"
"sync"

_ "runtime/pprof"

"github.com/Bios-Marcel/spoon/internal/cli"
"github.com/Bios-Marcel/spoon/pkg/scoop"
jsoniter "github.com/json-iterator/go"
Expand Down
8 changes: 4 additions & 4 deletions cmd/spoon/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,9 @@ func shellCmd() *cobra.Command {
}

if err := windows.CreateJunctions([][2]string{
{defaultScoop.GetCacheDir(), tempScoop.GetCacheDir()},
{defaultScoop.GetScoopInstallationDir(), tempScoop.GetScoopInstallationDir()},
{defaultScoop.GetBucketsDir(), tempScoop.GetBucketsDir()},
{defaultScoop.CacheDir(), tempScoop.CacheDir()},
{defaultScoop.ScoopInstallationDir(), tempScoop.ScoopInstallationDir()},
{defaultScoop.BucketDir(), tempScoop.BucketDir()},
}...); err != nil {
return fmt.Errorf("error creating junctions: %w", err)
}
Expand Down Expand Up @@ -211,7 +211,7 @@ func shellCmd() *cobra.Command {
// environment variables and some apps use env_add_path instead
// of specifying shims.
var app *scoop.InstalledApp
app, err = tempScoop.GetInstalledApp(dependency)
app, err = tempScoop.FindInstalledApp(dependency)
if err != nil {
break
}
Expand Down
Loading

0 comments on commit a913d81

Please sign in to comment.