Exemple #1
0
func aptlyRepoShow(cmd *commander.Command, args []string) error {
	var err error
	if len(args) != 1 {
		cmd.Usage()
		return err
	}

	name := args[0]

	localRepoCollection := debian.NewLocalRepoCollection(context.database)
	repo, err := localRepoCollection.ByName(name)
	if err != nil {
		return fmt.Errorf("unable to show: %s", err)
	}

	err = localRepoCollection.LoadComplete(repo)
	if err != nil {
		return fmt.Errorf("unable to show: %s", err)
	}

	fmt.Printf("Name: %s\n", repo.Name)
	fmt.Printf("Comment: %s\n", repo.Comment)
	fmt.Printf("Number of packages: %d\n", repo.NumPackages())

	withPackages := cmd.Flag.Lookup("with-packages").Value.Get().(bool)
	if withPackages {
		ListPackagesRefList(repo.RefList())
	}

	return err
}
Exemple #2
0
func aptlyRepoList(cmd *commander.Command, args []string) error {
	var err error
	if len(args) != 0 {
		cmd.Usage()
		return err
	}

	localRepoCollection := debian.NewLocalRepoCollection(context.database)

	if localRepoCollection.Len() > 0 {
		fmt.Printf("List of mirrors:\n")
		repos := make([]string, localRepoCollection.Len())
		i := 0
		localRepoCollection.ForEach(func(repo *debian.LocalRepo) error {
			err := localRepoCollection.LoadComplete(repo)
			if err != nil {
				return err
			}

			repos[i] = fmt.Sprintf(" * %s (packages: %d)", repo.String(), repo.NumPackages())
			i++
			return nil
		})

		sort.Strings(repos)
		for _, repo := range repos {
			fmt.Println(repo)
		}

		fmt.Printf("\nTo get more information about local repository, run `aptly repo show <name>`.\n")
	} else {
		fmt.Printf("No local repositories found, create one with `aptly repo create ...`.\n")
	}
	return err
}
Exemple #3
0
func aptlyRepoRemove(cmd *commander.Command, args []string) error {
	var err error
	if len(args) < 2 {
		cmd.Usage()
		return err
	}

	name := args[0]

	localRepoCollection := debian.NewLocalRepoCollection(context.database)
	repo, err := localRepoCollection.ByName(name)
	if err != nil {
		return fmt.Errorf("unable to remove: %s", err)
	}

	err = localRepoCollection.LoadComplete(repo)
	if err != nil {
		return fmt.Errorf("unable to remove: %s", err)
	}

	context.progress.Printf("Loading packages...\n")

	packageCollection := debian.NewPackageCollection(context.database)
	list, err := debian.NewPackageListFromRefList(repo.RefList(), packageCollection)
	if err != nil {
		return fmt.Errorf("unable to load packages: %s", err)
	}

	list.PrepareIndex()
	toRemove, err := list.Filter(args[1:], false, nil, 0, nil)
	if err != nil {
		return fmt.Errorf("unable to remove: %s", err)
	}

	toRemove.ForEach(func(p *debian.Package) error {
		list.Remove(p)
		context.progress.ColoredPrintf("@r[-]@| %s removed", p)
		return nil
	})

	if cmd.Flag.Lookup("dry-run").Value.Get().(bool) {
		context.progress.Printf("\nChanges not saved, as dry run has been requested.\n")
	} else {
		repo.UpdateRefList(debian.NewPackageRefListFromPackageList(list))

		err = localRepoCollection.Update(repo)
		if err != nil {
			return fmt.Errorf("unable to save: %s", err)
		}
	}

	return err
}
Exemple #4
0
func aptlyRepoCreate(cmd *commander.Command, args []string) error {
	var err error
	if len(args) != 1 {
		cmd.Usage()
		return err
	}

	repo := debian.NewLocalRepo(args[0], cmd.Flag.Lookup("comment").Value.String())

	localRepoCollection := debian.NewLocalRepoCollection(context.database)

	err = localRepoCollection.Add(repo)
	if err != nil {
		return fmt.Errorf("unable to add local repo: %s", err)
	}

	fmt.Printf("\nLocal repo %s successfully added.\nYou can run 'aptly repo add %s ...' to add packages to repository.\n", repo, repo.Name)
	return err
}
Exemple #5
0
func aptlyRepoDrop(cmd *commander.Command, args []string) error {
	var err error
	if len(args) != 1 {
		cmd.Usage()
		return err
	}

	name := args[0]

	localRepoCollection := debian.NewLocalRepoCollection(context.database)
	repo, err := localRepoCollection.ByName(name)
	if err != nil {
		return fmt.Errorf("unable to drop: %s", err)
	}

	force := cmd.Flag.Lookup("force").Value.Get().(bool)
	if !force {
		snapshotCollection := debian.NewSnapshotCollection(context.database)
		snapshots := snapshotCollection.ByLocalRepoSource(repo)

		if len(snapshots) > 0 {
			fmt.Printf("Local repo `%s` was used to create following snapshots:\n", repo.Name)
			for _, snapshot := range snapshots {
				fmt.Printf(" * %s\n", snapshot)
			}

			return fmt.Errorf("won't delete local repo with snapshots, use -force to override")
		}
	}

	err = localRepoCollection.Drop(repo)
	if err != nil {
		return fmt.Errorf("unable to drop: %s", err)
	}

	fmt.Printf("Local repo `%s` has been removed.\n", repo.Name)

	return err
}
Exemple #6
0
func aptlyRepoMoveCopyImport(cmd *commander.Command, args []string) error {
	var err error
	if len(args) < 3 {
		cmd.Usage()
		return err
	}

	command := cmd.Name()

	localRepoCollection := debian.NewLocalRepoCollection(context.database)

	dstRepo, err := localRepoCollection.ByName(args[1])
	if err != nil {
		return fmt.Errorf("unable to %s: %s", command, err)
	}

	err = localRepoCollection.LoadComplete(dstRepo)
	if err != nil {
		return fmt.Errorf("unable to %s: %s", command, err)
	}

	var (
		srcRefList *debian.PackageRefList
		srcRepo    *debian.LocalRepo
	)

	if command == "copy" || command == "move" {
		srcRepo, err = localRepoCollection.ByName(args[0])
		if err != nil {
			return fmt.Errorf("unable to %s: %s", command, err)
		}

		if srcRepo.UUID == dstRepo.UUID {
			return fmt.Errorf("unable to %s: source and destination are the same", command)
		}

		err = localRepoCollection.LoadComplete(srcRepo)
		if err != nil {
			return fmt.Errorf("unable to %s: %s", command, err)
		}

		srcRefList = srcRepo.RefList()
	} else if command == "import" {
		repoCollection := debian.NewRemoteRepoCollection(context.database)

		srcRepo, err := repoCollection.ByName(args[0])
		if err != nil {
			return fmt.Errorf("unable to %s: %s", command, err)
		}

		err = repoCollection.LoadComplete(srcRepo)
		if err != nil {
			return fmt.Errorf("unable to %s: %s", command, err)
		}

		if srcRepo.RefList() == nil {
			return fmt.Errorf("unable to %s: mirror not updated", command)
		}

		srcRefList = srcRepo.RefList()
	} else {
		panic("unexpected command")
	}

	context.progress.Printf("Loading packages...\n")

	packageCollection := debian.NewPackageCollection(context.database)
	dstList, err := debian.NewPackageListFromRefList(dstRepo.RefList(), packageCollection)
	if err != nil {
		return fmt.Errorf("unable to load packages: %s", err)
	}

	srcList, err := debian.NewPackageListFromRefList(srcRefList, packageCollection)
	if err != nil {
		return fmt.Errorf("unable to load packages: %s", err)
	}

	srcList.PrepareIndex()

	var architecturesList []string

	withDeps := cmd.Flag.Lookup("with-deps").Value.Get().(bool)

	if withDeps {
		dstList.PrepareIndex()

		// Calculate architectures
		if len(context.architecturesList) > 0 {
			architecturesList = context.architecturesList
		} else {
			architecturesList = dstList.Architectures(false)
		}

		sort.Strings(architecturesList)

		if len(architecturesList) == 0 {
			return fmt.Errorf("unable to determine list of architectures, please specify explicitly")
		}
	}

	toProcess, err := srcList.Filter(args[2:], withDeps, dstList, context.dependencyOptions, architecturesList)
	if err != nil {
		return fmt.Errorf("unable to %s: %s", command, err)
	}

	var verb string

	if command == "move" {
		verb = "moved"
	} else if command == "copy" {
		verb = "copied"
	} else if command == "import" {
		verb = "imported"
	}

	err = toProcess.ForEach(func(p *debian.Package) error {
		err = dstList.Add(p)
		if err != nil {
			return err
		}

		if command == "move" {
			srcList.Remove(p)
		}
		context.progress.ColoredPrintf("@g[o]@| %s %s", p, verb)
		return nil
	})
	if err != nil {
		return fmt.Errorf("unable to %s: %s", command, err)
	}

	if cmd.Flag.Lookup("dry-run").Value.Get().(bool) {
		context.progress.Printf("\nChanges not saved, as dry run has been requested.\n")
	} else {
		dstRepo.UpdateRefList(debian.NewPackageRefListFromPackageList(dstList))

		err = localRepoCollection.Update(dstRepo)
		if err != nil {
			return fmt.Errorf("unable to save: %s", err)
		}

		if command == "move" {
			srcRepo.UpdateRefList(debian.NewPackageRefListFromPackageList(srcList))

			err = localRepoCollection.Update(srcRepo)
			if err != nil {
				return fmt.Errorf("unable to save: %s", err)
			}
		}
	}

	return err
}
Exemple #7
0
func aptlyGraph(cmd *commander.Command, args []string) error {
	var err error

	graph := gographviz.NewGraph()
	graph.SetDir(true)
	graph.SetName("aptly")

	existingNodes := map[string]bool{}

	fmt.Printf("Loading mirrors...\n")

	repoCollection := debian.NewRemoteRepoCollection(context.database)

	err = repoCollection.ForEach(func(repo *debian.RemoteRepo) error {
		err := repoCollection.LoadComplete(repo)
		if err != nil {
			return err
		}

		graph.AddNode("aptly", graphvizEscape(repo.UUID), map[string]string{
			"shape":     "Mrecord",
			"style":     "filled",
			"fillcolor": "darkgoldenrod1",
			"label": graphvizEscape(fmt.Sprintf("{Mirror %s|url: %s|dist: %s|comp: %s|arch: %s|pkgs: %d}",
				repo.Name, repo.ArchiveRoot, repo.Distribution, strings.Join(repo.Components, ", "),
				strings.Join(repo.Architectures, ", "), repo.NumPackages())),
		})
		existingNodes[repo.UUID] = true
		return nil
	})

	if err != nil {
		return err
	}

	fmt.Printf("Loading local repos...\n")

	localRepoCollection := debian.NewLocalRepoCollection(context.database)

	err = localRepoCollection.ForEach(func(repo *debian.LocalRepo) error {
		err := localRepoCollection.LoadComplete(repo)
		if err != nil {
			return err
		}

		graph.AddNode("aptly", graphvizEscape(repo.UUID), map[string]string{
			"shape":     "Mrecord",
			"style":     "filled",
			"fillcolor": "mediumseagreen",
			"label": graphvizEscape(fmt.Sprintf("{Repo %s|comment: %s|pkgs: %d}",
				repo.Name, repo.Comment, repo.NumPackages())),
		})
		existingNodes[repo.UUID] = true
		return nil
	})

	if err != nil {
		return err
	}

	fmt.Printf("Loading snapshots...\n")

	snapshotCollection := debian.NewSnapshotCollection(context.database)

	snapshotCollection.ForEach(func(snapshot *debian.Snapshot) error {
		existingNodes[snapshot.UUID] = true
		return nil
	})

	err = snapshotCollection.ForEach(func(snapshot *debian.Snapshot) error {
		err := snapshotCollection.LoadComplete(snapshot)
		if err != nil {
			return err
		}

		description := snapshot.Description
		if snapshot.SourceKind == "repo" {
			description = "Snapshot from repo"
		}

		graph.AddNode("aptly", graphvizEscape(snapshot.UUID), map[string]string{
			"shape":     "Mrecord",
			"style":     "filled",
			"fillcolor": "cadetblue1",
			"label":     graphvizEscape(fmt.Sprintf("{Snapshot %s|%s|pkgs: %d}", snapshot.Name, description, snapshot.NumPackages())),
		})

		if snapshot.SourceKind == "repo" || snapshot.SourceKind == "local" || snapshot.SourceKind == "snapshot" {
			for _, uuid := range snapshot.SourceIDs {
				_, exists := existingNodes[uuid]
				if exists {
					graph.AddEdge(graphvizEscape(uuid), "", graphvizEscape(snapshot.UUID), "", true, nil)
				}
			}
		}
		return nil
	})

	if err != nil {
		return err
	}

	fmt.Printf("Loading published repos...\n")

	publishedCollection := debian.NewPublishedRepoCollection(context.database)

	publishedCollection.ForEach(func(repo *debian.PublishedRepo) error {
		graph.AddNode("aptly", graphvizEscape(repo.UUID), map[string]string{
			"shape":     "Mrecord",
			"style":     "filled",
			"fillcolor": "darkolivegreen1",
			"label":     graphvizEscape(fmt.Sprintf("{Published %s/%s|comp: %s|arch: %s}", repo.Prefix, repo.Distribution, repo.Component, strings.Join(repo.Architectures, ", "))),
		})

		_, exists := existingNodes[repo.SnapshotUUID]
		if exists {
			graph.AddEdge(graphvizEscape(repo.SnapshotUUID), "", graphvizEscape(repo.UUID), "", true, nil)
		}

		return nil
	})

	fmt.Printf("Generating graph...\n")

	buf := bytes.NewBufferString(graph.String())

	tempfile, err := ioutil.TempFile("", "aptly-graph")
	if err != nil {
		return err
	}
	tempfile.Close()
	os.Remove(tempfile.Name())

	tempfilename := tempfile.Name() + ".png"

	command := exec.Command("dot", "-Tpng", "-o"+tempfilename)
	command.Stderr = os.Stderr

	stdin, err := command.StdinPipe()
	if err != nil {
		return err
	}

	err = command.Start()
	if err != nil {
		return fmt.Errorf("unable to execute dot: %s (is graphviz package installed?)", err)
	}

	_, err = io.Copy(stdin, buf)
	if err != nil {
		return err
	}

	err = stdin.Close()
	if err != nil {
		return err
	}

	err = command.Wait()
	if err != nil {
		return err
	}

	err = exec.Command("open", tempfilename).Run()
	if err != nil {
		fmt.Printf("Rendered to PNG file: %s\n", tempfilename)
		err = nil
	}

	return err
}
Exemple #8
0
func aptlyRepoAdd(cmd *commander.Command, args []string) error {
	var err error
	if len(args) < 2 {
		cmd.Usage()
		return err
	}

	name := args[0]

	verifier := &utils.GpgVerifier{}

	localRepoCollection := debian.NewLocalRepoCollection(context.database)
	repo, err := localRepoCollection.ByName(name)
	if err != nil {
		return fmt.Errorf("unable to add: %s", err)
	}

	err = localRepoCollection.LoadComplete(repo)
	if err != nil {
		return fmt.Errorf("unable to add: %s", err)
	}

	context.progress.Printf("Loading packages...\n")

	packageCollection := debian.NewPackageCollection(context.database)
	list, err := debian.NewPackageListFromRefList(repo.RefList(), packageCollection)
	if err != nil {
		return fmt.Errorf("unable to load packages: %s", err)
	}

	packageFiles := []string{}

	for _, location := range args[1:] {
		info, err := os.Stat(location)
		if err != nil {
			context.progress.ColoredPrintf("@y[!]@| @!Unable to process %s: %s@|", location, err)
			continue
		}
		if info.IsDir() {
			err = filepath.Walk(location, func(path string, info os.FileInfo, err error) error {
				if err != nil {
					return err
				}
				if info.IsDir() {
					return nil
				}

				if strings.HasSuffix(info.Name(), ".deb") || strings.HasSuffix(info.Name(), ".dsc") {
					packageFiles = append(packageFiles, path)
				}

				return nil
			})
		} else {
			if strings.HasSuffix(info.Name(), ".deb") || strings.HasSuffix(info.Name(), ".dsc") {
				packageFiles = append(packageFiles, location)
			} else {
				context.progress.ColoredPrintf("@y[!]@| @!Unknwon file extenstion: %s@|", location)
				continue
			}
		}
	}

	processedFiles := []string{}
	sort.Strings(packageFiles)

	for _, file := range packageFiles {
		var (
			stanza debian.Stanza
			err    error
			p      *debian.Package
		)

		isSourcePackage := strings.HasSuffix(file, ".dsc")

		if isSourcePackage {
			stanza, err = debian.GetControlFileFromDsc(file, verifier)

			if err == nil {
				stanza["Package"] = stanza["Source"]
				delete(stanza, "Source")

				p, err = debian.NewSourcePackageFromControlFile(stanza)
			}
		} else {
			stanza, err = debian.GetControlFileFromDeb(file)
			p = debian.NewPackageFromControlFile(stanza)
		}
		if err != nil {
			context.progress.ColoredPrintf("@y[!]@| @!Unable to read file %s: %s@|", file, err)
			continue
		}

		checksums, err := utils.ChecksumsForFile(file)
		if err != nil {
			return err
		}

		if isSourcePackage {
			p.Files = append(p.Files, debian.PackageFile{Filename: filepath.Base(file), Checksums: checksums})
		} else {
			p.Files = []debian.PackageFile{debian.PackageFile{Filename: filepath.Base(file), Checksums: checksums}}
		}

		err = context.packagePool.Import(file, checksums.MD5)
		if err != nil {
			context.progress.ColoredPrintf("@y[!]@| @!Unable to import file %s into pool: %s@|", file, err)
			continue
		}

		processedFiles = append(processedFiles, file)

		// go over all files, except for the last one (.dsc/.deb itself)
		for i := 0; i < len(p.Files)-1; i++ {
			sourceFile := filepath.Join(filepath.Dir(file), filepath.Base(p.Files[i].Filename))
			err = context.packagePool.Import(sourceFile, p.Files[i].Checksums.MD5)
			if err != nil {
				context.progress.ColoredPrintf("@y[!]@| @!Unable to import file %s into pool: %s@|", sourceFile, err)
				break
			}

			processedFiles = append(processedFiles, sourceFile)
		}
		if err != nil {
			// some files haven't been imported
			continue
		}

		err = packageCollection.Update(p)
		if err != nil {
			context.progress.ColoredPrintf("@y[!]@| @!Unable to save package %s: %s@|", p, err)
			continue
		}

		err = list.Add(p)
		if err != nil {
			context.progress.ColoredPrintf("@y[!]@| @!Unable to add package to repo %s: %s@|", p, err)
			continue
		}

		context.progress.ColoredPrintf("@g[+]@| %s added@|", p)
	}

	repo.UpdateRefList(debian.NewPackageRefListFromPackageList(list))

	err = localRepoCollection.Update(repo)
	if err != nil {
		return fmt.Errorf("unable to save: %s", err)
	}

	if cmd.Flag.Lookup("remove-files").Value.Get().(bool) {
		processedFiles = utils.StrSliceDeduplicate(processedFiles)

		for _, file := range processedFiles {
			err := os.Remove(file)
			if err != nil {
				return fmt.Errorf("unable to remove file: %s", err)
			}
		}
	}

	return err
}
Exemple #9
0
func aptlySnapshotCreate(cmd *commander.Command, args []string) error {
	var (
		err      error
		snapshot *debian.Snapshot
	)

	if len(args) == 4 && args[1] == "from" && args[2] == "mirror" {
		// aptly snapshot create snap from mirror mirror
		repoName, snapshotName := args[3], args[0]

		repoCollection := debian.NewRemoteRepoCollection(context.database)
		repo, err := repoCollection.ByName(repoName)
		if err != nil {
			return fmt.Errorf("unable to create snapshot: %s", err)
		}

		err = repoCollection.LoadComplete(repo)
		if err != nil {
			return fmt.Errorf("unable to create snapshot: %s", err)
		}

		snapshot, err = debian.NewSnapshotFromRepository(snapshotName, repo)
		if err != nil {
			return fmt.Errorf("unable to create snapshot: %s", err)
		}
	} else if len(args) == 4 && args[1] == "from" && args[2] == "repo" {
		// aptly snapshot create snap from repo repo
		localRepoName, snapshotName := args[3], args[0]

		localRepoCollection := debian.NewLocalRepoCollection(context.database)
		repo, err := localRepoCollection.ByName(localRepoName)
		if err != nil {
			return fmt.Errorf("unable to create snapshot: %s", err)
		}

		err = localRepoCollection.LoadComplete(repo)
		if err != nil {
			return fmt.Errorf("unable to create snapshot: %s", err)
		}

		snapshot, err = debian.NewSnapshotFromLocalRepo(snapshotName, repo)
		if err != nil {
			return fmt.Errorf("unable to create snapshot: %s", err)
		}
	} else if len(args) == 2 && args[1] == "empty" {
		// aptly snapshot create snap empty
		snapshotName := args[0]

		packageList := debian.NewPackageList()

		snapshot = debian.NewSnapshotFromPackageList(snapshotName, nil, packageList, "Created as empty")
	} else {
		cmd.Usage()
		return err
	}

	snapshotCollection := debian.NewSnapshotCollection(context.database)

	err = snapshotCollection.Add(snapshot)
	if err != nil {
		return fmt.Errorf("unable to add snapshot: %s", err)
	}

	fmt.Printf("\nSnapshot %s successfully created.\nYou can run 'aptly publish snapshot %s' to publish snapshot as Debian repository.\n", snapshot.Name, snapshot.Name)

	return err
}
Exemple #10
0
// aptly db cleanup
func aptlyDbCleanup(cmd *commander.Command, args []string) error {
	var err error

	if len(args) != 0 {
		cmd.Usage()
		return err
	}

	// collect information about references packages...
	existingPackageRefs := debian.NewPackageRefList()

	context.progress.Printf("Loading mirrors, local repos and snapshots...\n")
	repoCollection := debian.NewRemoteRepoCollection(context.database)
	err = repoCollection.ForEach(func(repo *debian.RemoteRepo) error {
		err := repoCollection.LoadComplete(repo)
		if err != nil {
			return err
		}
		if repo.RefList() != nil {
			existingPackageRefs = existingPackageRefs.Merge(repo.RefList(), false)
		}
		return nil
	})
	if err != nil {
		return err
	}

	localRepoCollection := debian.NewLocalRepoCollection(context.database)
	err = localRepoCollection.ForEach(func(repo *debian.LocalRepo) error {
		err := localRepoCollection.LoadComplete(repo)
		if err != nil {
			return err
		}
		if repo.RefList() != nil {
			existingPackageRefs = existingPackageRefs.Merge(repo.RefList(), false)
		}
		return nil
	})
	if err != nil {
		return err
	}

	snapshotCollection := debian.NewSnapshotCollection(context.database)
	err = snapshotCollection.ForEach(func(snapshot *debian.Snapshot) error {
		err := snapshotCollection.LoadComplete(snapshot)
		if err != nil {
			return err
		}
		existingPackageRefs = existingPackageRefs.Merge(snapshot.RefList(), false)
		return nil
	})
	if err != nil {
		return err
	}

	// ... and compare it to the list of all packages
	context.progress.Printf("Loading list of all packages...\n")
	packageCollection := debian.NewPackageCollection(context.database)
	allPackageRefs := packageCollection.AllPackageRefs()

	toDelete := allPackageRefs.Substract(existingPackageRefs)

	// delete packages that are no longer referenced
	context.progress.Printf("Deleting unreferenced packages (%d)...\n", toDelete.Len())

	context.database.StartBatch()
	err = toDelete.ForEach(func(ref []byte) error {
		return packageCollection.DeleteByKey(ref)
	})
	if err != nil {
		return err
	}

	err = context.database.FinishBatch()
	if err != nil {
		return fmt.Errorf("unable to write to DB: %s", err)
	}

	// now, build a list of files that should be present in Repository (package pool)
	context.progress.Printf("Building list of files referenced by packages...\n")
	referencedFiles := make([]string, 0, existingPackageRefs.Len())
	context.progress.InitBar(int64(existingPackageRefs.Len()), false)

	err = existingPackageRefs.ForEach(func(key []byte) error {
		pkg, err := packageCollection.ByKey(key)
		if err != nil {
			return err
		}
		paths, err := pkg.FilepathList(context.packagePool)
		if err != nil {
			return err
		}
		referencedFiles = append(referencedFiles, paths...)
		context.progress.AddBar(1)

		return nil
	})
	if err != nil {
		return err
	}

	sort.Strings(referencedFiles)
	context.progress.ShutdownBar()

	// build a list of files in the package pool
	context.progress.Printf("Building list of files in package pool...\n")
	existingFiles, err := context.packagePool.FilepathList(context.progress)
	if err != nil {
		return fmt.Errorf("unable to collect file paths: %s", err)
	}

	// find files which are in the pool but not referenced by packages
	filesToDelete := utils.StrSlicesSubstract(existingFiles, referencedFiles)

	// delete files that are no longer referenced
	context.progress.Printf("Deleting unreferenced files (%d)...\n", len(filesToDelete))

	if len(filesToDelete) > 0 {
		context.progress.InitBar(int64(len(filesToDelete)), false)
		totalSize := int64(0)
		for _, file := range filesToDelete {
			size, err := context.packagePool.Remove(file)
			if err != nil {
				return err
			}

			context.progress.AddBar(1)
			totalSize += size
		}
		context.progress.ShutdownBar()

		context.progress.Printf("Disk space freed: %.2f GiB...\n", float64(totalSize)/1024.0/1024.0/1024.0)
	}
	return err
}