Browse Source

cmd/fdroidcl: merge upgraade into install

Now, install handles upgrades too. It will succeed if it can install a
new app, upgrade an existing app, or if an app is already up to date.

For the time being, this removes the "upgrade only if already installed"
and "install only if not already installed" features, but those are very
specific and likely not useful. They can be re-added as options if
necessary.

Fixes #23.
pull/32/head
Daniel Martí 8 years ago
parent
commit
b6e3746645
  1. 17
      adb/device.go
  2. 16
      cmd/fdroidcl/endtoend_test.go
  3. 27
      cmd/fdroidcl/install.go
  4. 7
      cmd/fdroidcl/main.go
  5. 60
      cmd/fdroidcl/upgrade.go

17
adb/device.go

@ -136,13 +136,8 @@ func getAbis(props map[string]string) []string {
var installFailureRegex = regexp.MustCompile(`^Failure \[INSTALL_(.+)\]$`)
func withOpts(cmd string, opts []string, args ...string) []string {
v := append([]string{cmd}, opts...)
return append(v, args...)
}
func (d *Device) install(opts []string, path string) error {
cmd := d.AdbCmd(withOpts("install", opts, path)...)
func (d *Device) Install(path string) error {
cmd := d.AdbCmd(append([]string{"install", "-r"}, path)...)
output, err := cmd.CombinedOutput()
if err != nil {
return err
@ -154,14 +149,6 @@ func (d *Device) install(opts []string, path string) error {
return parseError(getFailureCode(installFailureRegex, line))
}
func (d *Device) Install(path string) error {
return d.install(nil, path)
}
func (d *Device) Upgrade(path string) error {
return d.install([]string{"-r"}, path)
}
func getResultLine(output []byte) string {
scanner := bufio.NewScanner(bytes.NewReader(output))
for scanner.Scan() {

16
cmd/fdroidcl/endtoend_test.go

@ -128,15 +128,15 @@ func TestCommands(t *testing.T) {
t.Run("SearchUpgradable", func(t *testing.T) {
mustSucceed(t, regexp.QuoteMeta(chosenApp), ``, cmdSearch, "-u", "-q")
})
t.Run("Upgrade", func(t *testing.T) {
mustSucceed(t, `Upgrading `+regexp.QuoteMeta(chosenApp), ``,
cmdUpgrade, chosenApp)
t.Run("InstallUpgrade", func(t *testing.T) {
mustSucceed(t, `Installing `+regexp.QuoteMeta(chosenApp), ``,
cmdInstall, chosenApp)
})
t.Run("SearchUpgradableUpToDate", func(t *testing.T) {
mustSucceed(t, ``, regexp.QuoteMeta(chosenApp), cmdSearch, "-u", "-q")
})
t.Run("UpgradeAlreadyInstalled", func(t *testing.T) {
mustFail(t, `is up to date$`, ``, cmdUpgrade, chosenApp)
t.Run("InstallUpToDate", func(t *testing.T) {
mustSucceed(t, `is up to date$`, ``, cmdInstall, chosenApp)
})
t.Run("UninstallExisting", func(t *testing.T) {
mustSucceed(t, `Uninstalling `+regexp.QuoteMeta(chosenApp), ``,
@ -149,14 +149,14 @@ func mustRun(t *testing.T, success bool, wantRe, negRe string, cmd *Command, arg
stdout, stderr = &buf, &buf
err := cmd.Run(args)
out := buf.String()
if err != nil {
out += err.Error()
}
if success && err != nil {
t.Fatalf("unexpected error: %v\n%s", err, out)
} else if !success && err == nil {
t.Fatalf("expected error, got none\n%s", out)
}
if err != nil {
out += err.Error()
}
// Let '.' match newlines, and treat the output as a single line.
wantRe = "(?sm)" + wantRe
if !regexp.MustCompile(wantRe).MatchString(out) {

27
cmd/fdroidcl/install.go

@ -12,7 +12,7 @@ import (
var cmdInstall = &Command{
UsageLine: "install <appid...>",
Short: "Install an app",
Short: "Install or upgrade an app",
}
func init() {
@ -35,15 +35,30 @@ func runInstall(args []string) error {
if err != nil {
return err
}
var toInstall []*fdroidcl.App
for _, app := range apps {
if _, e := inst[app.ID]; e {
return fmt.Errorf("%s is already installed", app.ID)
p, e := inst[app.ID]
if !e {
// installing an app from scratch
toInstall = append(toInstall, app)
continue
}
suggested := app.SuggestedApk(device)
if suggested == nil {
return fmt.Errorf("no suitable APKs found for %s", app.ID)
}
if p.VCode >= suggested.VCode {
fmt.Fprintf(stdout, "%s is up to date\n", app.ID)
// app is already up to date
continue
}
// upgrading an existing app
toInstall = append(toInstall, app)
}
return downloadAndDo(apps, device, installApk)
return downloadAndDo(toInstall, device)
}
func downloadAndDo(apps []*fdroidcl.App, device *adb.Device, doApk func(*adb.Device, *fdroidcl.Apk, string) error) error {
func downloadAndDo(apps []*fdroidcl.App, device *adb.Device) error {
type downloaded struct {
apk *fdroidcl.Apk
path string
@ -61,7 +76,7 @@ func downloadAndDo(apps []*fdroidcl.App, device *adb.Device, doApk func(*adb.Dev
toInstall[i] = downloaded{apk: apk, path: path}
}
for _, t := range toInstall {
if err := doApk(device, t.apk, t.path); err != nil {
if err := installApk(device, t.apk, t.path); err != nil {
return err
}
}

7
cmd/fdroidcl/main.go

@ -162,12 +162,11 @@ var commands = []*Command{
cmdUpdate,
cmdSearch,
cmdShow,
cmdList,
cmdDevices,
cmdDownload,
cmdInstall,
cmdUpgrade,
cmdUninstall,
cmdDownload,
cmdDevices,
cmdList,
cmdDefaults,
cmdVersion,
}

60
cmd/fdroidcl/upgrade.go

@ -1,60 +0,0 @@
// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information
package main
import (
"fmt"
"mvdan.cc/fdroidcl"
"mvdan.cc/fdroidcl/adb"
)
var cmdUpgrade = &Command{
UsageLine: "upgrade <appid...>",
Short: "Upgrade an app",
}
func init() {
cmdUpgrade.Run = runUpgrade
}
func runUpgrade(args []string) error {
if len(args) < 1 {
return fmt.Errorf("no package names given")
}
apps, err := findApps(args)
if err != nil {
return err
}
device, err := oneDevice()
if err != nil {
return err
}
inst, err := device.Installed()
if err != nil {
return err
}
for _, app := range apps {
p, e := inst[app.ID]
if !e {
return fmt.Errorf("%s is not installed", app.ID)
}
suggested := app.SuggestedApk(device)
if suggested == nil {
return fmt.Errorf("no suitable APKs found for %s", app.ID)
}
if p.VCode >= suggested.VCode {
return fmt.Errorf("%s is up to date", app.ID)
}
}
return downloadAndDo(apps, device, upgradeApk)
}
func upgradeApk(device *adb.Device, apk *fdroidcl.Apk, path string) error {
fmt.Fprintf(stdout, "Upgrading %s\n", apk.AppID)
if err := device.Upgrade(path); err != nil {
return fmt.Errorf("could not upgrade %s: %v", apk.AppID, err)
}
return nil
}
Loading…
Cancel
Save