feat(v12): add group install (#1835)

v12engine: add group install
This commit is contained in:
Jo 2022-11-20 02:47:23 +00:00 committed by GitHub
parent 6ad63cae10
commit 9f67d10d5c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 231 additions and 154 deletions

View file

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"os"
"sync"
"github.com/Jguer/yay/v11/pkg/db"
"github.com/Jguer/yay/v11/pkg/dep"
@ -12,6 +13,7 @@ import (
"github.com/Jguer/yay/v11/pkg/settings/parser"
"github.com/Jguer/yay/v11/pkg/text"
gosrc "github.com/Morganamilo/go-srcinfo"
mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext"
)
@ -48,10 +50,11 @@ func (installer *Installer) Install(ctx context.Context,
cmdArgs *parser.Arguments,
targets []map[string]*dep.InstallInfo,
pkgBuildDirs map[string]string,
srcinfos map[string]*gosrc.Srcinfo,
) error {
// Reorganize targets into layers of dependencies
for i := len(targets) - 1; i >= 0; i-- {
err := installer.handleLayer(ctx, cmdArgs, targets[i], pkgBuildDirs)
err := installer.handleLayer(ctx, cmdArgs, targets[i], pkgBuildDirs, srcinfos)
if err != nil {
// rollback
return err
@ -62,7 +65,10 @@ func (installer *Installer) Install(ctx context.Context,
}
func (installer *Installer) handleLayer(ctx context.Context,
cmdArgs *parser.Arguments, layer map[string]*dep.InstallInfo, pkgBuildDirs map[string]string,
cmdArgs *parser.Arguments,
layer map[string]*dep.InstallInfo,
pkgBuildDirs map[string]string,
srcinfos map[string]*gosrc.Srcinfo,
) error {
// Install layer
nameToBaseMap := make(map[string]string, 0)
@ -107,7 +113,8 @@ func (installer *Installer) handleLayer(ctx context.Context,
return ErrInstallRepoPkgs
}
errAur := installer.installAURPackages(ctx, cmdArgs, aurDeps, aurExp, nameToBaseMap, pkgBuildDirs, false)
errAur := installer.installAURPackages(ctx, cmdArgs, aurDeps, aurExp,
nameToBaseMap, pkgBuildDirs, true, srcinfos)
return errAur
}
@ -117,11 +124,22 @@ func (installer *Installer) installAURPackages(ctx context.Context,
aurDepNames, aurExpNames mapset.Set[string],
nameToBase, pkgBuildDirsByBase map[string]string,
installIncompatible bool,
srcinfos map[string]*gosrc.Srcinfo,
) error {
all := aurDepNames.Union(aurExpNames).ToSlice()
if len(all) == 0 {
return nil
}
deps, exps := make([]string, 0, aurDepNames.Cardinality()), make([]string, 0, aurExpNames.Cardinality())
pkgArchives := make([]string, 0, len(exps)+len(deps))
for _, name := range aurDepNames.Union(aurExpNames).ToSlice() {
var (
mux sync.Mutex
wg sync.WaitGroup
)
for _, name := range all {
base := nameToBase[name]
dir := pkgBuildDirsByBase[base]
args := []string{"--nobuild", "-fC"}
@ -169,8 +187,14 @@ func (installer *Installer) installAURPackages(ctx context.Context,
if hasDebug {
deps = append(deps, name+"-debug")
}
srcinfo := srcinfos[base]
wg.Add(1)
go config.Runtime.VCSStore.Update(ctx, name, srcinfo.Source, &mux, &wg)
}
wg.Wait()
if err := installPkgArchive(ctx, cmdArgs, pkgArchives); err != nil {
return fmt.Errorf("%s - %w", fmt.Sprintf(gotext.Get("error installing:")+" %v", pkgArchives), err)
}

View file

@ -75,7 +75,6 @@ func asexp(ctx context.Context, cmdArgs *parser.Arguments, pkgs []string) error
// Install handles package installs.
func install(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Executor, ignoreProviders bool) error {
var (
incompatible stringset.StringSet
do *dep.Order
srcinfos map[string]*gosrc.Srcinfo
noDeps = cmdArgs.ExistsDouble("d", "nodeps")
@ -270,11 +269,11 @@ func install(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Execu
text.Errorln(errDiffMenu)
}
if errM := mergePkgbuilds(ctx, do.Aur); errM != nil {
if errM := mergePkgbuilds(ctx, pkgbuildDirs); errM != nil {
return errM
}
srcinfos, err = parseSrcinfoFiles(do.Aur, true)
srcinfos, err = parseSrcinfoFiles(pkgbuildDirs, true)
if err != nil {
return err
}
@ -289,13 +288,12 @@ func install(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Execu
text.Errorln(errEditMenu)
}
incompatible, err = getIncompatible(do.Aur, srcinfos, dbExecutor)
if err != nil {
return err
if errI := confirmIncompatibleInstall(srcinfos, dbExecutor); errI != nil {
return errI
}
if config.PGPFetch {
if errCPK := pgp.CheckPgpKeys(do.Aur, srcinfos, config.GpgBin, config.GpgFlags, settings.NoConfirm); errCPK != nil {
if _, errCPK := pgp.CheckPgpKeys(pkgbuildDirs, srcinfos, config.GpgBin, config.GpgFlags, settings.NoConfirm); errCPK != nil {
return errCPK
}
}
@ -341,19 +339,14 @@ func install(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Execu
config.AURURL, config.Runtime.CompletionPath, config.CompletionInterval, false)
}()
pkgBuildDirs := make(map[string]string, len(do.Aur))
for _, base := range do.Aur {
pkgBuildDirs[base.Pkgbase()] = filepath.Join(config.BuildDir, base.Pkgbase())
}
if errP := downloadPKGBUILDSourceFanout(ctx,
config.Runtime.CmdBuilder,
pkgBuildDirs,
len(incompatible) > 0, config.MaxConcurrentDownloads); errP != nil {
pkgbuildDirs,
true, config.MaxConcurrentDownloads); errP != nil {
text.Errorln(errP)
}
if errB := buildInstallPkgbuilds(ctx, cmdArgs, dbExecutor, dp, do, srcinfos, incompatible, conflicts, noDeps, noCheck); errB != nil {
if errB := buildInstallPkgbuilds(ctx, cmdArgs, dbExecutor, dp, do, srcinfos, true, conflicts, noDeps, noCheck); errB != nil {
return errB
}
@ -461,42 +454,39 @@ func earlyRefresh(ctx context.Context, cmdArgs *parser.Arguments) error {
arguments, config.Runtime.Mode, settings.NoConfirm))
}
func getIncompatible(bases []dep.Base, srcinfos map[string]*gosrc.Srcinfo, dbExecutor db.Executor) (stringset.StringSet, error) {
incompatible := make(stringset.StringSet)
basesMap := make(map[string]dep.Base)
func confirmIncompatibleInstall(srcinfos map[string]*gosrc.Srcinfo, dbExecutor db.Executor) error {
incompatible := []string{}
alpmArch, err := dbExecutor.AlpmArchitectures()
if err != nil {
return nil, err
return err
}
nextpkg:
for _, base := range bases {
for _, arch := range srcinfos[base.Pkgbase()].Arch {
for base, srcinfo := range srcinfos {
for _, arch := range srcinfo.Arch {
if db.ArchIsSupported(alpmArch, arch) {
continue nextpkg
}
}
incompatible.Set(base.Pkgbase())
basesMap[base.Pkgbase()] = base
incompatible = append(incompatible, base)
}
if len(incompatible) > 0 {
text.Warnln(gotext.Get("The following packages are not compatible with your architecture:"))
for pkg := range incompatible {
fmt.Print(" " + text.Cyan(basesMap[pkg].String()))
for _, pkg := range incompatible {
fmt.Print(" " + text.Cyan(pkg))
}
fmt.Println()
if !text.ContinueTask(os.Stdin, gotext.Get("Try to build them anyway?"), true, settings.NoConfirm) {
return nil, &settings.ErrUserAbort{}
return &settings.ErrUserAbort{}
}
}
return incompatible, nil
return nil
}
func parsePackageList(ctx context.Context, dir string) (pkgdests map[string]string, pkgVersion string, err error) {
@ -532,26 +522,25 @@ func parsePackageList(ctx context.Context, dir string) (pkgdests map[string]stri
return pkgdests, pkgVersion, nil
}
func parseSrcinfoFiles(bases []dep.Base, errIsFatal bool) (map[string]*gosrc.Srcinfo, error) {
func parseSrcinfoFiles(pkgBuildDirs map[string]string, errIsFatal bool) (map[string]*gosrc.Srcinfo, error) {
srcinfos := make(map[string]*gosrc.Srcinfo)
for k, base := range bases {
pkg := base.Pkgbase()
dir := filepath.Join(config.BuildDir, pkg)
text.OperationInfoln(gotext.Get("(%d/%d) Parsing SRCINFO: %s", k+1, len(bases), text.Cyan(base.String())))
k := 0
for base, dir := range pkgBuildDirs {
text.OperationInfoln(gotext.Get("(%d/%d) Parsing SRCINFO: %s", k+1, len(pkgBuildDirs), text.Cyan(base)))
pkgbuild, err := gosrc.ParseFile(filepath.Join(dir, ".SRCINFO"))
if err != nil {
if !errIsFatal {
text.Warnln(gotext.Get("failed to parse %s -- skipping: %s", base.String(), err))
text.Warnln(gotext.Get("failed to parse %s -- skipping: %s", base, err))
continue
}
return nil, errors.New(gotext.Get("failed to parse %s: %s", base.String(), err))
return nil, errors.New(gotext.Get("failed to parse %s: %s", base, err))
}
srcinfos[pkg] = pkgbuild
srcinfos[base] = pkgbuild
k++
}
return srcinfos, nil
@ -583,27 +572,27 @@ func pkgbuildsToSkip(bases []dep.Base, targets stringset.StringSet) stringset.St
return toSkip
}
func gitMerge(ctx context.Context, path, name string) error {
func gitMerge(ctx context.Context, dir string) error {
_, stderr, err := config.Runtime.CmdBuilder.Capture(
config.Runtime.CmdBuilder.BuildGitCmd(ctx,
filepath.Join(path, name), "reset", "--hard", "HEAD"))
dir, "reset", "--hard", "HEAD"))
if err != nil {
return errors.New(gotext.Get("error resetting %s: %s", name, stderr))
return errors.New(gotext.Get("error resetting %s: %s", dir, stderr))
}
_, stderr, err = config.Runtime.CmdBuilder.Capture(
config.Runtime.CmdBuilder.BuildGitCmd(ctx,
filepath.Join(path, name), "merge", "--no-edit", "--ff"))
dir, "merge", "--no-edit", "--ff"))
if err != nil {
return errors.New(gotext.Get("error merging %s: %s", name, stderr))
return errors.New(gotext.Get("error merging %s: %s", dir, stderr))
}
return nil
}
func mergePkgbuilds(ctx context.Context, bases []dep.Base) error {
for _, base := range bases {
err := gitMerge(ctx, config.BuildDir, base.Pkgbase())
func mergePkgbuilds(ctx context.Context, pkgbuildDirs map[string]string) error {
for _, dir := range pkgbuildDirs {
err := gitMerge(ctx, dir)
if err != nil {
return err
}
@ -619,7 +608,7 @@ func buildInstallPkgbuilds(
dp *dep.Pool,
do *dep.Order,
srcinfos map[string]*gosrc.Srcinfo,
incompatible stringset.StringSet,
incompatible bool,
conflicts stringset.MapStringSet, noDeps, noCheck bool,
) error {
deps := make([]string, 0)
@ -680,7 +669,7 @@ func buildInstallPkgbuilds(
args := []string{"--nobuild", "-fC"}
if incompatible.Get(pkg) {
if incompatible {
args = append(args, "--ignorearch")
}
@ -749,7 +738,7 @@ func buildInstallPkgbuilds(
} else {
args := []string{"-cf", "--noconfirm", "--noextract", "--noprepare", "--holdver"}
if incompatible.Get(pkg) {
if incompatible {
args = append(args, "--ignorearch")
}

View file

@ -58,24 +58,27 @@ func installLocalPKGBUILD(
}
installer := &Installer{dbExecutor: dbExecutor}
if errP := preparer.Present(os.Stdout, topoSorted); errP != nil {
return errP
pkgBuildDirs, err := preparer.Run(ctx, os.Stdout, topoSorted)
if err != nil {
return err
}
if cleanFunc := preparer.ShouldCleanMakeDeps(); cleanFunc != nil {
installer.AddPostInstallHook(cleanFunc)
}
pkgBuildDirs, err := preparer.PrepareWorkspace(ctx, topoSorted)
if err != nil {
return err
}
if cleanAURDirsFunc := preparer.ShouldCleanAURDirs(pkgBuildDirs); cleanAURDirsFunc != nil {
installer.AddPostInstallHook(cleanAURDirsFunc)
}
if err = installer.Install(ctx, cmdArgs, topoSorted, pkgBuildDirs); err != nil {
srcinfoOp := srcinfoOperator{dbExecutor: dbExecutor}
srcinfos, err := srcinfoOp.Run(pkgBuildDirs)
if err != nil {
return err
}
if err = installer.Install(ctx, cmdArgs, topoSorted, pkgBuildDirs, srcinfos); err != nil {
if errHook := installer.RunPostInstallHooks(ctx); errHook != nil {
text.Errorln(errHook)
}

View file

@ -130,7 +130,7 @@ func (g *Grapher) GraphFromTargets(ctx context.Context,
if pkg := g.dbExecutor.SyncPackage(target.Name); pkg != nil {
dbName := pkg.DB().Name()
graph.AddNode(pkg.Name())
g.ValidateAndSetNodeInfo(graph, target.Name, &topo.NodeInfo[*InstallInfo]{
g.ValidateAndSetNodeInfo(graph, pkg.Name(), &topo.NodeInfo[*InstallInfo]{
Color: colorMap[Explicit],
Background: bgColorMap[Sync],
Value: &InstallInfo{
@ -144,6 +144,24 @@ func (g *Grapher) GraphFromTargets(ctx context.Context,
continue
}
groupPackages := g.dbExecutor.PackagesFromGroup(target.Name)
if len(groupPackages) > 0 {
dbName := groupPackages[0].DB().Name()
graph.AddNode(target.Name)
g.ValidateAndSetNodeInfo(graph, target.Name, &topo.NodeInfo[*InstallInfo]{
Color: colorMap[Explicit],
Background: bgColorMap[Sync],
Value: &InstallInfo{
Source: Sync,
Reason: Explicit,
Version: "",
SyncDBName: &dbName,
},
})
continue
}
fallthrough
case "aur":
graph, err = g.GraphFromAURCache(ctx, graph, []string{target.Name})

View file

@ -25,6 +25,10 @@ func anyExistInCache(pkgbuildDirs map[string]string) bool {
}
func CleanFn(ctx context.Context, config *settings.Configuration, w io.Writer, pkgbuildDirsByBase map[string]string) error {
if len(pkgbuildDirsByBase) == 0 {
return nil // no work to do
}
if !anyExistInCache(pkgbuildDirsByBase) {
return nil
}

View file

@ -182,6 +182,10 @@ func Diff(ctx context.Context, cmdBuilder exe.ICmdBuilder, w io.Writer,
}
func DiffFn(ctx context.Context, config *settings.Configuration, w io.Writer, pkgbuildDirsByBase map[string]string) error {
if len(pkgbuildDirsByBase) == 0 {
return nil // no work to do
}
bases := make([]string, 0, len(pkgbuildDirsByBase))
for base := range pkgbuildDirsByBase {
bases = append(bases, base)

View file

@ -150,6 +150,10 @@ func Edit(w io.Writer, editMenuOption bool, pkgbuildDirs map[string]string, edit
func EditFn(ctx context.Context, config *settings.Configuration, w io.Writer,
pkgbuildDirsByBase map[string]string,
) error {
if len(pkgbuildDirsByBase) == 0 {
return nil // no work to do
}
bases := make([]string, 0, len(pkgbuildDirsByBase))
for pkg := range pkgbuildDirsByBase {
bases = append(bases, pkg)

View file

@ -1,5 +0,0 @@
 -> ABAF11C65A2970B130ABE3C479BE3E4300411886, required by: dummy-1 (dummy-1 dummy-2)
:: Importing keys with gpg...
:: PGP keys need importing:

View file

@ -1,5 +0,0 @@
 -> 487EACC08557AD082088DABA1EB2638FF56C0C53, required by: cower
:: Importing keys with gpg...
:: PGP keys need importing:

View file

@ -1,5 +0,0 @@
 -> C52048C0C0748FEE227D47A2702353E0F7E48EDB, required by: dummy-3
:: Importing keys with gpg...
:: PGP keys need importing:

View file

@ -1,6 +0,0 @@
 -> 11E521D646982372EB577A1F8F0871F202119294, required by: libc++
 -> B6C8F98282B944E3B0D5C2530FC3042E345AD05D, required by: libc++
:: Importing keys with gpg...
:: PGP keys need importing:

View file

@ -11,13 +11,12 @@ import (
gosrc "github.com/Morganamilo/go-srcinfo"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v11/pkg/dep"
"github.com/Jguer/yay/v11/pkg/text"
)
// pgpKeySet maps a PGP key with a list of PKGBUILDs that require it.
// This is similar to stringSet, used throughout the code.
type pgpKeySet map[string][]dep.Base
type pgpKeySet map[string][]string
func (set pgpKeySet) toSlice() []string {
slice := make([]string, 0, len(set))
@ -28,7 +27,7 @@ func (set pgpKeySet) toSlice() []string {
return slice
}
func (set pgpKeySet) set(key string, p dep.Base) {
func (set pgpKeySet) set(key, p string) {
// Using ToUpper to make sure keys with a different case will be
// considered the same.
upperKey := strings.ToUpper(key)
@ -44,9 +43,9 @@ func (set pgpKeySet) get(key string) bool {
// CheckPgpKeys iterates through the keys listed in the PKGBUILDs and if needed,
// asks the user whether yay should try to import them.
func CheckPgpKeys(bases []dep.Base, srcinfos map[string]*gosrc.Srcinfo,
func CheckPgpKeys(pkgbuildDirsByBase map[string]string, srcinfos map[string]*gosrc.Srcinfo,
gpgBin, gpgFlags string, noConfirm bool,
) error {
) ([]string, error) {
// Let's check the keys individually, and then we can offer to import
// the problematic ones.
problematic := make(pgpKeySet)
@ -54,43 +53,42 @@ func CheckPgpKeys(bases []dep.Base, srcinfos map[string]*gosrc.Srcinfo,
args := append(strings.Fields(gpgFlags), "--list-keys")
// Mapping all the keys.
for _, base := range bases {
pkg := base.Pkgbase()
for pkg := range pkgbuildDirsByBase {
srcinfo := srcinfos[pkg]
for _, key := range srcinfo.ValidPGPKeys {
// If key already marked as problematic, indicate the current
// PKGBUILD requires it.
if problematic.get(key) {
problematic.set(key, base)
problematic.set(key, pkg)
continue
}
cmd := exec.Command(gpgBin, append(args, key)...)
if err := cmd.Run(); err != nil {
problematic.set(key, base)
problematic.set(key, pkg)
}
}
}
// No key issues!
if len(problematic) == 0 {
return nil
return []string{}, nil
}
str, err := formatKeysToImport(problematic)
if err != nil {
return err
return nil, err
}
fmt.Println()
fmt.Println(str)
if text.ContinueTask(os.Stdin, gotext.Get("Import?"), true, noConfirm) {
return importKeys(problematic.toSlice(), gpgBin, gpgFlags)
return problematic.toSlice(), importKeys(problematic.toSlice(), gpgBin, gpgFlags)
}
return nil
return problematic.toSlice(), nil
}
// importKeys tries to import the list of keys specified in its argument.
@ -122,7 +120,7 @@ func formatKeysToImport(keys pgpKeySet) (string, error) {
for key, bases := range keys {
pkglist := ""
for _, base := range bases {
pkglist += base.String() + " "
pkglist += base + " "
}
pkglist = strings.TrimRight(pkglist, " ")

View file

@ -4,21 +4,16 @@ import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"os"
"path"
"regexp"
"sort"
"strings"
"testing"
"time"
aur "github.com/Jguer/aur"
gosrc "github.com/Morganamilo/go-srcinfo"
"github.com/bradleyjkemp/cupaloy"
"github.com/Jguer/yay/v11/pkg/dep"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const (
@ -42,10 +37,6 @@ func init() {
})
}
func newPkg(basename string) *aur.Pkg {
return &aur.Pkg{Name: basename, PackageBase: basename}
}
func getPgpKey(key string) string {
var buffer bytes.Buffer
@ -159,40 +150,44 @@ func TestCheckPgpKeys(t *testing.T) {
casetests := []struct {
name string
pkgs dep.Base
pkgs map[string]string
srcinfos map[string]*gosrc.Srcinfo
wantError bool
expected []string
}{
// cower: single package, one valid key not yet in the keyring.
// 487EACC08557AD082088DABA1EB2638FF56C0C53: Dave Reisner.
{
name: " one valid key not yet in the keyring",
pkgs: dep.Base{newPkg("cower")},
pkgs: map[string]string{"cower": ""},
srcinfos: map[string]*gosrc.Srcinfo{"cower": makeSrcinfo("cower", "487EACC08557AD082088DABA1EB2638FF56C0C53")},
wantError: false,
expected: []string{"487EACC08557AD082088DABA1EB2638FF56C0C53"},
},
// libc++: single package, two valid keys not yet in the keyring.
// 11E521D646982372EB577A1F8F0871F202119294: Tom Stellard.
// B6C8F98282B944E3B0D5C2530FC3042E345AD05D: Hans Wennborg.
{
name: "two valid keys not yet in the keyring",
pkgs: dep.Base{newPkg("libc++")},
pkgs: map[string]string{"libc++": ""},
srcinfos: map[string]*gosrc.Srcinfo{
"libc++": makeSrcinfo("libc++", "11E521D646982372EB577A1F8F0871F202119294", "B6C8F98282B944E3B0D5C2530FC3042E345AD05D"),
},
wantError: false,
expected: []string{"11E521D646982372EB577A1F8F0871F202119294", "B6C8F98282B944E3B0D5C2530FC3042E345AD05D"},
},
// Two dummy packages requiring the same key.
// ABAF11C65A2970B130ABE3C479BE3E4300411886: Linus Torvalds.
{
name: "Two dummy packages requiring the same key",
pkgs: dep.Base{newPkg("dummy-1"), newPkg("dummy-2")},
pkgs: map[string]string{"dummy-1": "", "dummy-2": ""},
srcinfos: map[string]*gosrc.Srcinfo{
"dummy-1": makeSrcinfo("dummy-1",
"ABAF11C65A2970B130ABE3C479BE3E4300411886"),
"dummy-2": makeSrcinfo("dummy-2", "ABAF11C65A2970B130ABE3C479BE3E4300411886"),
},
wantError: false,
expected: []string{"ABAF11C65A2970B130ABE3C479BE3E4300411886"},
},
// dummy package: single package, two valid keys, one of them already
// in the keyring.
@ -200,26 +195,28 @@ func TestCheckPgpKeys(t *testing.T) {
// C52048C0C0748FEE227D47A2702353E0F7E48EDB: Thomas Dickey.
{
name: "one already in keyring",
pkgs: dep.Base{newPkg("dummy-3")},
pkgs: map[string]string{"dummy-3": ""},
srcinfos: map[string]*gosrc.Srcinfo{
"dummy-3": makeSrcinfo("dummy-3", "11E521D646982372EB577A1F8F0871F202119294", "C52048C0C0748FEE227D47A2702353E0F7E48EDB"),
},
wantError: false,
expected: []string{"C52048C0C0748FEE227D47A2702353E0F7E48EDB"},
},
// Two dummy packages with existing keys.
{
name: "two existing",
pkgs: dep.Base{newPkg("dummy-4"), newPkg("dummy-5")},
pkgs: map[string]string{"dummy-4": "", "dummy-5": ""},
srcinfos: map[string]*gosrc.Srcinfo{
"dummy-4": makeSrcinfo("dummy-4", "11E521D646982372EB577A1F8F0871F202119294"),
"dummy-5": makeSrcinfo("dummy-5", "C52048C0C0748FEE227D47A2702353E0F7E48EDB"),
},
wantError: false,
expected: []string{},
},
// Dummy package with invalid key, should fail.
{
name: "one invalid",
pkgs: dep.Base{newPkg("dummy-7")},
pkgs: map[string]string{"dummy-7": ""},
srcinfos: map[string]*gosrc.Srcinfo{"dummy-7": makeSrcinfo("dummy-7", "THIS-SHOULD-FAIL")},
wantError: true,
},
@ -227,40 +224,27 @@ func TestCheckPgpKeys(t *testing.T) {
// A314827C4E4250A204CE6E13284FC34C8E4B1A25: Thomas Bächler.
{
name: "one invalid, one valid",
pkgs: dep.Base{newPkg("dummy-8")},
pkgs: map[string]string{"dummy-8": ""},
srcinfos: map[string]*gosrc.Srcinfo{"dummy-8": makeSrcinfo("dummy-8", "A314827C4E4250A204CE6E13284FC34C8E4B1A25", "THIS-SHOULD-FAIL")},
wantError: true,
expected: []string{},
},
}
for _, tt := range casetests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
rescueStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
err := CheckPgpKeys([]dep.Base{tt.pkgs}, tt.srcinfos, "gpg",
problematic, err := CheckPgpKeys(tt.pkgs, tt.srcinfos, "gpg",
fmt.Sprintf("--homedir %s --keyserver 127.0.0.1", keyringDir), true)
if !tt.wantError {
if err != nil {
t.Fatalf("Got error %q, want no error", err)
}
w.Close()
out, _ := io.ReadAll(r)
os.Stdout = rescueStdout
splitLines := strings.Split(string(out), "\n")
sort.Strings(splitLines)
cupaloy.SnapshotT(t, strings.Join(splitLines, "\n"))
if tt.wantError {
require.Error(t, err)
return
}
// Here, we want to see the error.
if err == nil {
t.Fatalf("Got no error; want error")
}
require.NoError(t, err)
assert.ElementsMatch(t, tt.expected, problematic, fmt.Sprintf("%#v", problematic))
})
}
}

View file

@ -21,13 +21,14 @@ import (
"github.com/leonelquinteros/gotext"
)
type PostDownloadHookFunc func(ctx context.Context, config *settings.Configuration, w io.Writer, pkgbuildDirsByBase map[string]string) error
type PreparerHookFunc func(ctx context.Context, config *settings.Configuration, w io.Writer, pkgbuildDirsByBase map[string]string) error
type Preparer struct {
dbExecutor db.Executor
cmdBuilder exe.ICmdBuilder
config *settings.Configuration
postDownloadHooks []PostDownloadHookFunc
postDownloadHooks []PreparerHookFunc
postMergeHooks []PreparerHookFunc
makeDeps []string
}
@ -37,7 +38,8 @@ func NewPreparer(dbExecutor db.Executor, cmdBuilder exe.ICmdBuilder, config *set
dbExecutor: dbExecutor,
cmdBuilder: cmdBuilder,
config: config,
postDownloadHooks: []PostDownloadHookFunc{},
postDownloadHooks: []PreparerHookFunc{},
postMergeHooks: []PreparerHookFunc{},
}
if config.CleanMenu {
@ -45,11 +47,11 @@ func NewPreparer(dbExecutor db.Executor, cmdBuilder exe.ICmdBuilder, config *set
}
if config.DiffMenu {
preper.postDownloadHooks = append(preper.postDownloadHooks, menus.DiffFn)
preper.postMergeHooks = append(preper.postMergeHooks, menus.DiffFn)
}
if config.EditMenu {
preper.postDownloadHooks = append(preper.postDownloadHooks, menus.EditFn)
preper.postMergeHooks = append(preper.postMergeHooks, menus.EditFn)
}
return preper
@ -91,7 +93,20 @@ func (preper *Preparer) ShouldCleanMakeDeps() PostInstallHookFunc {
}
}
func (preper *Preparer) Present(w io.Writer, targets []map[string]*dep.InstallInfo) error {
func (preper *Preparer) Run(ctx context.Context,
w io.Writer, targets []map[string]*dep.InstallInfo,
) (pkgbuildDirsByBase map[string]string, err error) {
preper.Present(w, targets)
pkgBuildDirs, err := preper.PrepareWorkspace(ctx, targets)
if err != nil {
return nil, err
}
return pkgBuildDirs, nil
}
func (preper *Preparer) Present(w io.Writer, targets []map[string]*dep.InstallInfo) {
pkgsBySourceAndReason := map[string]map[string][]string{}
for _, layer := range targets {
@ -127,8 +142,6 @@ func (preper *Preparer) Present(w io.Writer, targets []map[string]*dep.InstallIn
strings.Join(pkgs, ", "))
}
}
return nil
}
func (preper *Preparer) PrepareWorkspace(ctx context.Context, targets []map[string]*dep.InstallInfo) (map[string]string, error) {
@ -168,6 +181,16 @@ func (preper *Preparer) PrepareWorkspace(ctx context.Context, targets []map[stri
}
}
if err := mergePkgbuilds(ctx, pkgBuildDirsByBase); err != nil {
return nil, err
}
for _, hookFn := range preper.postMergeHooks {
if err := hookFn(ctx, preper.config, os.Stdout, pkgBuildDirsByBase); err != nil {
return nil, err
}
}
return pkgBuildDirsByBase, nil
}
@ -179,6 +202,9 @@ func (preper *Preparer) needToCloneAURBase(installInfo *dep.InstallInfo, pkgbuil
srcinfoFile := filepath.Join(pkgbuildDir, ".SRCINFO")
if pkgbuild, err := gosrc.ParseFile(srcinfoFile); err == nil {
if db.VerCmp(pkgbuild.Version(), installInfo.Version) >= 0 {
text.OperationInfoln(
gotext.Get("PKGBUILD up to date, skipping download: %s",
text.Cyan(*installInfo.AURBase)))
return false
}
}

32
srcinfo.go Normal file
View file

@ -0,0 +1,32 @@
package main
import (
"github.com/Jguer/yay/v11/pkg/db"
"github.com/Jguer/yay/v11/pkg/pgp"
"github.com/Jguer/yay/v11/pkg/settings"
gosrc "github.com/Morganamilo/go-srcinfo"
)
type srcinfoOperator struct {
dbExecutor db.Executor
}
func (s *srcinfoOperator) Run(pkgbuildDirs map[string]string) (map[string]*gosrc.Srcinfo, error) {
srcinfos, err := parseSrcinfoFiles(pkgbuildDirs, true)
if err != nil {
return nil, err
}
if err := confirmIncompatibleInstall(srcinfos, s.dbExecutor); err != nil {
return nil, err
}
if config.PGPFetch {
if _, errCPK := pgp.CheckPgpKeys(pkgbuildDirs, srcinfos, config.GpgBin, config.GpgFlags, settings.NoConfirm); errCPK != nil {
return nil, errCPK
}
}
return srcinfos, nil
}

25
sync.go
View file

@ -5,6 +5,7 @@ import (
"fmt"
"os"
"github.com/Jguer/yay/v11/pkg/completion"
"github.com/Jguer/yay/v11/pkg/db"
"github.com/Jguer/yay/v11/pkg/dep"
"github.com/Jguer/yay/v11/pkg/settings"
@ -55,8 +56,9 @@ func syncInstall(ctx context.Context,
preparer := NewPreparer(dbExecutor, config.Runtime.CmdBuilder, config)
installer := &Installer{dbExecutor: dbExecutor}
if errP := preparer.Present(os.Stdout, topoSorted); errP != nil {
return errP
pkgBuildDirs, err := preparer.Run(ctx, os.Stdout, topoSorted)
if err != nil {
return err
}
cleanFunc := preparer.ShouldCleanMakeDeps()
@ -64,16 +66,23 @@ func syncInstall(ctx context.Context,
installer.AddPostInstallHook(cleanFunc)
}
pkgBuildDirs, err := preparer.PrepareWorkspace(ctx, topoSorted)
if err != nil {
return err
}
if cleanAURDirsFunc := preparer.ShouldCleanAURDirs(pkgBuildDirs); cleanAURDirsFunc != nil {
installer.AddPostInstallHook(cleanAURDirsFunc)
}
err = installer.Install(ctx, cmdArgs, topoSorted, pkgBuildDirs)
srcinfoOp := srcinfoOperator{dbExecutor: dbExecutor}
srcinfos, err := srcinfoOp.Run(pkgBuildDirs)
if err != nil {
return err
}
go func() {
_ = completion.Update(ctx, config.Runtime.HTTPClient, dbExecutor,
config.AURURL, config.Runtime.CompletionPath, config.CompletionInterval, false)
}()
err = installer.Install(ctx, cmdArgs, topoSorted, pkgBuildDirs, srcinfos)
if err != nil {
if errHook := installer.RunPostInstallHooks(ctx); errHook != nil {
text.Errorln(errHook)

6
vcs.go
View file

@ -2,6 +2,7 @@ package main
import (
"context"
"path/filepath"
"strings"
"sync"
@ -33,11 +34,14 @@ func createDevelDB(ctx context.Context, config *settings.Configuration, dbExecut
toSkip := pkgbuildsToSkip(bases, stringset.FromSlice(remoteNames))
targets := make([]string, 0, len(bases))
pkgBuildDirsByBase := make(map[string]string, len(bases))
for _, base := range bases {
if !toSkip.Get(base.Pkgbase()) {
targets = append(targets, base.Pkgbase())
}
pkgBuildDirsByBase[base.Pkgbase()] = filepath.Join(config.BuildDir, base.Pkgbase())
}
toSkipSlice := toSkip.ToSlice()
@ -52,7 +56,7 @@ func createDevelDB(ctx context.Context, config *settings.Configuration, dbExecut
return err
}
srcinfos, err := parseSrcinfoFiles(bases, false)
srcinfos, err := parseSrcinfoFiles(pkgBuildDirsByBase, false)
if err != nil {
return err
}