Skip to content

Commit

Permalink
Add Conflicts and Replaces to the PkgSpec (#33)
Browse files Browse the repository at this point in the history
Package Conflicts will prevent the package from being installed if a conflicting package is already installed
Package Replaces will remove any packages that are replaced by one being installed
  • Loading branch information
adjackura authored Jan 3, 2018
1 parent b18e545 commit 41c7efb
Show file tree
Hide file tree
Showing 11 changed files with 206 additions and 56 deletions.
3 changes: 2 additions & 1 deletion googet.goospec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "googet",
"version": "2.12.0@1",
"version": "2.13.0@1",
"arch": "x86_64",
"authors": "[email protected]",
"license": "http://www.apache.org/licenses/LICENSE-2.0",
Expand All @@ -12,6 +12,7 @@
"path": "install.ps1"
},
"releaseNotes": [
"2.13.0 - Add Conflicts and Replaces fields to the GooGet PkgSpec.",
"2.12.0 - Store goo files unextracted in cache, ensure checksum match on reinstall.",
"2.11.0 - Add allowunsafeurl config option to enable HTTP repos, otherwise disable.",
" - Force the use of a package checksum when downloading from a repository.",
Expand Down
4 changes: 2 additions & 2 deletions googet_clean.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func cleanPackages(pl []string) {

for _, pkg := range *state {
if goolib.ContainsString(pkg.PackageSpec.Name, pl) {
if err := oswrap.RemoveAll(pkg.UnpackDir); err != nil {
if err := oswrap.RemoveAll(pkg.LocalPath); err != nil {
logger.Error(err)
}
}
Expand Down Expand Up @@ -95,7 +95,7 @@ func cleanOld() {

var il []string
for _, pkg := range *state {
il = append(il, pkg.UnpackDir)
il = append(il, pkg.LocalPath)
}
clean(il)
}
37 changes: 24 additions & 13 deletions googet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,19 +225,23 @@ func TestCleanOld(t *testing.T) {
}
defer oswrap.RemoveAll(rootDir)

wantDir := filepath.Join(rootDir, cacheDir, "want")
wantFile := filepath.Join(rootDir, cacheDir, "want.goo")
notWantDir := filepath.Join(rootDir, cacheDir, "notWant")
notWantFile := filepath.Join(rootDir, cacheDir, "notWant.goo")

if err := oswrap.MkdirAll(wantDir, 0700); err != nil {
if err := oswrap.MkdirAll(notWantDir, 0700); err != nil {
t.Fatal(err)
}
if err := oswrap.MkdirAll(notWantDir, 0700); err != nil {
if err := ioutil.WriteFile(notWantFile, nil, 0700); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(wantFile, nil, 0700); err != nil {
t.Fatal(err)
}

state := &client.GooGetState{
{
UnpackDir: wantDir,
LocalPath: wantFile,
},
}

Expand All @@ -247,13 +251,17 @@ func TestCleanOld(t *testing.T) {

cleanOld()

if _, err := oswrap.Stat(wantDir); err != nil {
if _, err := oswrap.Stat(wantFile); err != nil {
t.Errorf("cleanOld removed wantDir, Stat err: %v", err)
}

if _, err := oswrap.Stat(notWantDir); err == nil {
t.Errorf("cleanOld did not remove notWantDir")
}

if _, err := oswrap.Stat(notWantFile); err == nil {
t.Errorf("cleanOld did not remove notWantFile")
}
}

func TestCleanPackages(t *testing.T) {
Expand All @@ -264,25 +272,28 @@ func TestCleanPackages(t *testing.T) {
}
defer oswrap.RemoveAll(rootDir)

wantDir := filepath.Join(rootDir, cacheDir, "want")
notWantDir := filepath.Join(rootDir, cacheDir, "notWant")
wantFile := filepath.Join(rootDir, cacheDir, "want")
notWantFile := filepath.Join(rootDir, cacheDir, "notWant")

if err := oswrap.MkdirAll(wantDir, 0700); err != nil {
if err := oswrap.MkdirAll(filepath.Join(rootDir, cacheDir), 0700); err != nil {
t.Fatal(err)
}
if err := oswrap.MkdirAll(notWantDir, 0700); err != nil {
if err := ioutil.WriteFile(wantFile, nil, 0700); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(notWantFile, nil, 0700); err != nil {
t.Fatal(err)
}

state := &client.GooGetState{
{
UnpackDir: wantDir,
LocalPath: wantFile,
PackageSpec: &goolib.PkgSpec{
Name: "want",
},
},
{
UnpackDir: notWantDir,
LocalPath: notWantFile,
PackageSpec: &goolib.PkgSpec{
Name: "notWant",
},
Expand All @@ -295,11 +306,11 @@ func TestCleanPackages(t *testing.T) {

cleanPackages([]string{"notWant"})

if _, err := oswrap.Stat(wantDir); err != nil {
if _, err := oswrap.Stat(wantFile); err != nil {
t.Errorf("cleanPackages removed wantDir, Stat err: %v", err)
}

if _, err := oswrap.Stat(notWantDir); err == nil {
if _, err := oswrap.Stat(notWantFile); err == nil {
t.Errorf("cleanPackages did not remove notWantDir")
}
}
10 changes: 10 additions & 0 deletions goolib/goolib.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,16 @@ type PackageInfo struct {
Name, Arch, Ver string
}

func (pi PackageInfo) String() string {
if pi.Arch != "" && pi.Ver != "" {
return fmt.Sprintf("%s.%s.%s", pi.Name, pi.Arch, pi.Ver)
}
if pi.Arch != "" {
return fmt.Sprintf("%s.%s", pi.Name, pi.Arch)
}
return pi.Name
}

// PkgName returns the proper goo package name.
func (pi PackageInfo) PkgName() string {
return fmt.Sprintf("%s.%s.%s.goo", pi.Name, pi.Arch, pi.Ver)
Expand Down
4 changes: 3 additions & 1 deletion goolib/goospec.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const (

var validArch = []string{"noarch", "x86_64", "x86_32", "arm"}

// PkgSpec is the internal package specification.
// PkgSpec is an individual package specification.
type PkgSpec struct {
Name string
Version string
Expand All @@ -80,6 +80,8 @@ type PkgSpec struct {
Owners string `json:",omitempty"`
Tags map[string][]byte `json:",omitempty"`
PkgDependencies map[string]string `json:",omitempty"`
Replaces []string
Conflicts []string
Install ExecFile
Uninstall ExecFile
Files map[string]string `json:",omitempty"`
Expand Down
8 changes: 8 additions & 0 deletions goolib/goospec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,8 @@ func TestMarshal(t *testing.T) {
ReleaseNotes: []string{"1.2.3@4 - something new", "1.2.3@4 - something"},
Description: "blah blah",
Owners: "someone",
Replaces: []string{"foo"},
Conflicts: []string{"bar"},
Install: ExecFile{
Path: "install.ps1",
},
Expand All @@ -315,6 +317,12 @@ func TestMarshal(t *testing.T) {
],
"Description": "blah blah",
"Owners": "someone",
"Replaces": [
"foo"
],
"Conflicts": [
"bar"
],
"Install": {
"Path": "install.ps1"
},
Expand Down
66 changes: 64 additions & 2 deletions install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/google/googet/download"
"github.com/google/googet/goolib"
"github.com/google/googet/oswrap"
"github.com/google/googet/remove"
"github.com/google/googet/system"
"github.com/google/logger"
)
Expand All @@ -48,8 +49,51 @@ func minInstalled(pi goolib.PackageInfo, state client.GooGetState) (bool, error)
return false, nil
}

func resolveConflicts(ps *goolib.PkgSpec, state *client.GooGetState) error {
// Check for any conflicting packages.
// TODO(ajackura): Make sure no conflicting packages are listed as
// dependencies or subdependancies.
for _, pkg := range ps.Conflicts {
pi := goolib.PkgNameSplit(pkg)
ins, err := minInstalled(goolib.PackageInfo{Name: pi.Name, Arch: pi.Arch, Ver: pi.Ver}, *state)
if err != nil {
return err
}
if ins {
return fmt.Errorf("cannot install, conflict with installed package: %s", pi)
}
}
return nil
}

func resolveReplacements(ps *goolib.PkgSpec, state *client.GooGetState, dbOnly bool, proxyServer string) error {
// Check for and remove any package this replaces.
// TODO(ajackura): Make sure no replacements are listed as
// dependencies or subdependancies.
for _, pkg := range ps.Replaces {
pi := goolib.PkgNameSplit(pkg)
ins, err := minInstalled(goolib.PackageInfo{Name: pi.Name, Arch: pi.Arch, Ver: pi.Ver}, *state)
if err != nil {
return err
}
if !ins {
continue
}
deps, _ := remove.EnumerateDeps(pi, *state)
logger.Infof("%s replaces %s, removing", ps, pi)
if err := remove.All(pi, deps, state, dbOnly, proxyServer); err != nil {
return err
}
}
return nil
}

func installDeps(ps *goolib.PkgSpec, cache string, rm client.RepoMap, archs []string, state *client.GooGetState, dbOnly bool, proxyServer string) error {
logger.Infof("Resolving dependencies for %s %s version %s", ps.Arch, ps.Name, ps.Version)
logger.Infof("Resolving conflicts and dependencies for %s %s version %s", ps.Arch, ps.Name, ps.Version)
if err := resolveConflicts(ps, state); err != nil {
return err
}
// Check for and install any dependencies.
for p, ver := range ps.PkgDependencies {
pi := goolib.PkgNameSplit(p)
mi, err := minInstalled(goolib.PackageInfo{Name: pi.Name, Arch: pi.Arch, Ver: ver}, *state)
Expand Down Expand Up @@ -80,7 +124,7 @@ func installDeps(ps *goolib.PkgSpec, cache string, rm client.RepoMap, archs []st
return fmt.Errorf("cannot resolve dependancy, %s.%s version %s or greater not installed and not available in any repo", pi.Name, arch, ver)
}
}
return nil
return resolveReplacements(ps, state, dbOnly, proxyServer)
}

// FromRepo installs a package and all dependencies from a repository.
Expand Down Expand Up @@ -157,6 +201,9 @@ func FromDisk(arg, cache string, state *client.GooGetState, dbOnly, ri bool) err
logger.Infof("Starting install of %q, version %q from %q", zs.Name, zs.Version, arg)
fmt.Printf("Installing %s %s...\n", zs.Name, zs.Version)

if err := resolveConflicts(zs, state); err != nil {
return err
}
for p, ver := range zs.PkgDependencies {
pi := goolib.PkgNameSplit(p)
mi, err := minInstalled(goolib.PackageInfo{Name: pi.Name, Arch: pi.Arch, Ver: ver}, *state)
Expand All @@ -169,6 +216,16 @@ func FromDisk(arg, cache string, state *client.GooGetState, dbOnly, ri bool) err
}
return fmt.Errorf("package dependency %s %s (min version %s) not installed", pi.Name, pi.Arch, ver)
}
for _, pkg := range zs.Replaces {
pi := goolib.PkgNameSplit(pkg)
ins, err := minInstalled(goolib.PackageInfo{Name: pi.Name, Arch: pi.Arch, Ver: pi.Ver}, *state)
if err != nil {
return err
}
if ins {
return fmt.Errorf("cannot install, replaces installed package, remove first then try installation again: %s", pi)
}
}

dst := filepath.Join(cache, goolib.PackageInfo{Name: zs.Name, Arch: zs.Arch, Ver: zs.Version}.PkgName())
if err := copyPkg(arg, dst); err != nil {
Expand Down Expand Up @@ -208,6 +265,11 @@ func Reinstall(ps client.PackageState, state client.GooGetState, rd bool, proxyS
pi := goolib.PackageInfo{Name: ps.PackageSpec.Name, Arch: ps.PackageSpec.Arch, Ver: ps.PackageSpec.Version}
logger.Infof("Starting reinstall of %s.%s, version %s", pi.Name, pi.Arch, pi.Ver)
fmt.Printf("Reinstalling %s.%s %s and dependencies...\n", pi.Name, pi.Arch, pi.Ver)
// Fix for package install by older versions of GooGet.
if ps.LocalPath == "" {
ps.LocalPath = ps.UnpackDir + ".goo"
}

f, err := os.Open(ps.LocalPath)
if err != nil && !os.IsNotExist(err) {
return err
Expand Down
47 changes: 35 additions & 12 deletions remove/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,27 +33,50 @@ func uninstallPkg(pi goolib.PackageInfo, state *client.GooGetState, dbOnly bool,
if err != nil {
return fmt.Errorf("package not found in state file: %v", err)
}
// Fix for package install by older versions of GooGet.
if ps.LocalPath == "" {
ps.LocalPath = ps.UnpackDir + ".goo"
}
if !dbOnly {
_, err := oswrap.Stat(ps.UnpackDir)
f, err := os.Open(ps.LocalPath)
if err != nil && !os.IsNotExist(err) {
return err
}
var rd bool
if os.IsNotExist(err) {
dst := ps.UnpackDir + ".goo"
logger.Infof("Package directory does not exist for %s.%s.%s, redownloading...", ps.PackageSpec.Name, ps.PackageSpec.Arch, ps.PackageSpec.Version)
if err := download.Package(ps.DownloadURL, dst, ps.Checksum, proxyServer); err != nil {
return fmt.Errorf("error redownloading %s.%s.%s, package may no longer exist in the repo, you can use the '-db_only' flag to remove it form the database: %v", pi.Name, pi.Arch, pi.Ver, err)
}
if _, err := download.ExtractPkg(dst); err != nil {
return err
logger.Infof("Local package does not exist for %s.%s.%s, redownloading...", pi.Name, pi.Arch, pi.Ver)
rd = true
}
// Force redownload if checksum does not match.
// If checksum is empty this was a local install so ignore.
if !rd && ps.Checksum != "" && goolib.Checksum(f) != ps.Checksum {
logger.Info("Local package checksum does not match, redownloading...")
rd = true
}
f.Close()

if rd {
if ps.DownloadURL == "" {
return fmt.Errorf("can not redownload %s.%s.%s, DownloadURL not saved", pi.Name, pi.Arch, pi.Ver)
}
if err := oswrap.Remove(dst); err != nil {
logger.Errorf("error cleaning up package file: %v", err)
if err := download.Package(ps.DownloadURL, ps.LocalPath, ps.Checksum, proxyServer); err != nil {
return fmt.Errorf("error redownloading %s.%s.%s, package may no longer exist in the repo, you can use the '-db_only' flag to remove it form the database: %v", pi.Name, pi.Arch, pi.Ver, err)
}
}
if err := system.Uninstall(ps); err != nil {

eDir, err := download.ExtractPkg(ps.LocalPath)
if err != nil {
return err
}

if err := system.Uninstall(eDir, ps.PackageSpec); err != nil {
return err
}

if err := oswrap.RemoveAll(eDir); err != nil {
logger.Error(err)
}

if len(ps.InstalledFiles) > 0 {
var dirs []string
for file, chksum := range ps.InstalledFiles {
Expand All @@ -76,7 +99,7 @@ func uninstallPkg(pi goolib.PackageInfo, state *client.GooGetState, dbOnly bool,
}
}

if err := oswrap.RemoveAll(ps.UnpackDir); err != nil {
if err := oswrap.RemoveAll(ps.LocalPath); err != nil {
logger.Errorf("error removing package data from cache directory: %v", err)
}
return state.Remove(pi)
Expand Down
Loading

0 comments on commit 41c7efb

Please sign in to comment.