feat(new engine): local install feature testing (#1867)

* make config into parameter

* test(new engine): local install test

* test(keys): fix test keys

* complete integration test for local install

* add simple mising mechanism
This commit is contained in:
Jo
2022-12-29 12:34:53 +00:00
committed by GitHub
parent d3fbfa26ca
commit 28d90c981e
22 changed files with 685 additions and 254 deletions

28
cmd.go
View File

@@ -149,13 +149,13 @@ getpkgbuild specific options:
-p --print Print pkgbuild of packages`)
}
func handleCmd(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
func handleCmd(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
if cmdArgs.ExistsArg("h", "help") {
return handleHelp(ctx, cmdArgs)
}
if config.SudoLoop && cmdArgs.NeedRoot(config.Runtime.Mode) {
config.Runtime.CmdBuilder.SudoLoop()
if cfg.SudoLoop && cmdArgs.NeedRoot(cfg.Runtime.Mode) {
cfg.Runtime.CmdBuilder.SudoLoop()
}
switch cmdArgs.Op {
@@ -164,30 +164,30 @@ func handleCmd(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Exe
return nil
case "D", "database":
return config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, config.Runtime.Mode, settings.NoConfirm))
return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, cfg.Runtime.Mode, settings.NoConfirm))
case "F", "files":
return config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, config.Runtime.Mode, settings.NoConfirm))
return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, cfg.Runtime.Mode, settings.NoConfirm))
case "Q", "query":
return handleQuery(ctx, cmdArgs, dbExecutor)
case "R", "remove":
return handleRemove(ctx, cmdArgs, config.Runtime.VCSStore)
return handleRemove(ctx, cmdArgs, cfg.Runtime.VCSStore)
case "S", "sync":
return handleSync(ctx, cmdArgs, dbExecutor)
case "T", "deptest":
return config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, config.Runtime.Mode, settings.NoConfirm))
return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, cfg.Runtime.Mode, settings.NoConfirm))
case "U", "upgrade":
return handleUpgrade(ctx, config, cmdArgs)
return handleUpgrade(ctx, cfg, cmdArgs)
case "B", "build":
return handleBuild(ctx, config, dbExecutor, cmdArgs)
return handleBuild(ctx, cfg, dbExecutor, cmdArgs)
case "G", "getpkgbuild":
return handleGetpkgbuild(ctx, cmdArgs, dbExecutor)
case "P", "show":
return handlePrint(ctx, cmdArgs, dbExecutor)
case "Y", "yay":
return handleYay(ctx, cmdArgs, dbExecutor, config.Runtime.QueryBuilder)
return handleYay(ctx, cmdArgs, dbExecutor, cfg.Runtime.QueryBuilder)
case "W", "web":
return handleWeb(ctx, cmdArgs)
}
@@ -379,7 +379,7 @@ func handleSync(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Ex
return nil
}
func handleRemove(ctx context.Context, cmdArgs *parser.Arguments, localCache *vcs.InfoStore) error {
func handleRemove(ctx context.Context, cmdArgs *parser.Arguments, localCache vcs.Store) error {
err := config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, config.Runtime.Mode, settings.NoConfirm))
if err == nil {

View File

@@ -278,7 +278,7 @@ func install(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Execu
text.Errorln(errDiffMenu)
}
if errM := mergePkgbuilds(ctx, pkgbuildDirs); errM != nil {
if errM := mergePkgbuilds(ctx, config.Runtime.CmdBuilder, pkgbuildDirs); errM != nil {
return errM
}
@@ -302,7 +302,7 @@ func install(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Execu
}
if config.PGPFetch {
if _, errCPK := pgp.CheckPgpKeys(pkgbuildDirs, srcinfos, config.GpgBin, config.GpgFlags, settings.NoConfirm); errCPK != nil {
if _, errCPK := pgp.CheckPgpKeys(ctx, pkgbuildDirs, srcinfos, config.Runtime.CmdBuilder, settings.NoConfirm); errCPK != nil {
return errCPK
}
}
@@ -587,16 +587,16 @@ func pkgbuildsToSkip(bases []dep.Base, targets stringset.StringSet) stringset.St
return toSkip
}
func gitMerge(ctx context.Context, dir string) error {
_, stderr, err := config.Runtime.CmdBuilder.Capture(
config.Runtime.CmdBuilder.BuildGitCmd(ctx,
func gitMerge(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) error {
_, stderr, err := cmdBuilder.Capture(
cmdBuilder.BuildGitCmd(ctx,
dir, "reset", "--hard", "HEAD"))
if err != nil {
return errors.New(gotext.Get("error resetting %s: %s", dir, stderr))
}
_, stderr, err = config.Runtime.CmdBuilder.Capture(
config.Runtime.CmdBuilder.BuildGitCmd(ctx,
_, stderr, err = cmdBuilder.Capture(
cmdBuilder.BuildGitCmd(ctx,
dir, "merge", "--no-edit", "--ff"))
if err != nil {
return errors.New(gotext.Get("error merging %s: %s", dir, stderr))
@@ -605,9 +605,9 @@ func gitMerge(ctx context.Context, dir string) error {
return nil
}
func mergePkgbuilds(ctx context.Context, pkgbuildDirs map[string]string) error {
func mergePkgbuilds(ctx context.Context, cmdBuilder exe.ICmdBuilder, pkgbuildDirs map[string]string) error {
for _, dir := range pkgbuildDirs {
err := gitMerge(ctx, dir)
err := gitMerge(ctx, cmdBuilder, dir)
if err != nil {
return err
}
@@ -710,7 +710,7 @@ func buildInstallPkgbuilds(
for _, split := range base {
pkgdest, ok := pkgdests[split.Name]
if !ok {
return errors.New(gotext.Get("could not find PKGDEST for: %s", split.Name))
return &PkgDestNotInListError{split.Name}
}
if _, errStat := os.Stat(pkgdest); os.IsNotExist(errStat) {
@@ -894,7 +894,7 @@ func doAddTarget(dp *dep.Pool, localNamesCache, remoteNamesCache stringset.Strin
return deps, exp, pkgArchives, nil
}
return deps, exp, pkgArchives, errors.New(gotext.Get("could not find PKGDEST for: %s", name))
return deps, exp, pkgArchives, &PkgDestNotInListError{name}
}
if _, errStat := os.Stat(pkgdest); os.IsNotExist(errStat) {

View File

@@ -10,6 +10,7 @@ import (
"github.com/Jguer/yay/v11/pkg/db"
"github.com/Jguer/yay/v11/pkg/dep"
"github.com/Jguer/yay/v11/pkg/multierror"
"github.com/Jguer/yay/v11/pkg/settings"
"github.com/Jguer/yay/v11/pkg/settings/parser"
"github.com/Jguer/yay/v11/pkg/topo"
@@ -51,5 +52,16 @@ func installLocalPKGBUILD(
}
opService := NewOperationService(ctx, config, dbExecutor)
return opService.Run(ctx, cmdArgs, graph.TopoSortedLayerMap())
multiErr := &multierror.MultiError{}
targets := graph.TopoSortedLayerMap(func(name string, ii *dep.InstallInfo) error {
if ii.Source == dep.Missing {
multiErr.Add(errors.New(gotext.Get("could not find %s%s", name, ii.Version)))
}
return nil
})
if err := multiErr.Return(); err != nil {
return err
}
return opService.Run(ctx, cmdArgs, targets)
}

281
local_install_test.go Normal file
View File

@@ -0,0 +1,281 @@
package main
import (
"context"
"fmt"
"os"
"os/exec"
"strings"
"sync"
"testing"
aur "github.com/Jguer/aur"
"github.com/Jguer/aur/metadata"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v11/pkg/db/mock"
mockaur "github.com/Jguer/yay/v11/pkg/dep/mock"
"github.com/Jguer/yay/v11/pkg/settings"
"github.com/Jguer/yay/v11/pkg/settings/exe"
"github.com/Jguer/yay/v11/pkg/settings/parser"
"github.com/Jguer/yay/v11/pkg/vcs"
)
func TestIntegrationLocalInstall(t *testing.T) {
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
gitBin := t.TempDir() + "/git"
tmpDir := t.TempDir()
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
tars := []string{
tmpDir + "/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
}
wantShow := []string{
"makepkg --verifysource -Ccf",
"pacman -S --config /etc/pacman.conf -- community/dotnet-sdk-6.0 community/dotnet-runtime-6.0",
"pacman -D -q --asdeps --config /etc/pacman.conf -- dotnet-runtime-6.0 dotnet-sdk-6.0",
"makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst /testdir/jellyfin-10.8.4-1-x86_64.pkg.tar.zst /testdir/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin-server jellyfin jellyfin-web",
}
wantCapture := []string{
"makepkg --packagelist",
"git -C testdata/jfin git reset --hard HEAD",
"git -C testdata/jfin git merge --no-edit --ff",
"makepkg --packagelist",
"makepkg --packagelist",
}
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return strings.Join(tars, "\n"), "", nil
}
once := sync.Once{}
showOverride := func(cmd *exec.Cmd) error {
once.Do(func() {
for _, tar := range tars {
f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(t, err)
require.NoError(t, f.Close())
}
})
return nil
}
mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
PacmanConfigPath: "/etc/pacman.conf",
GitBin: "git",
Runner: mockRunner,
SudoLoopEnabled: false,
}
cmdArgs := parser.MakeArguments()
cmdArgs.AddArg("B")
cmdArgs.AddArg("i")
cmdArgs.AddTarget("testdata/jfin")
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "dotnet-sdk>=6", "dotnet-sdk<7", "dotnet-runtime>=6", "dotnet-runtime<7", "jellyfin-server=10.8.4", "jellyfin-web=10.8.4":
return false
}
return true
},
SyncSatisfierFn: func(s string) mock.IPackage {
switch s {
case "dotnet-runtime>=6", "dotnet-runtime<7":
return &mock.Package{
PName: "dotnet-runtime-6.0",
PBase: "dotnet-runtime-6.0",
PVersion: "6.0.100-1",
PDB: mock.NewDB("community"),
}
case "dotnet-sdk>=6", "dotnet-sdk<7":
return &mock.Package{
PName: "dotnet-sdk-6.0",
PBase: "dotnet-sdk-6.0",
PVersion: "6.0.100-1",
PDB: mock.NewDB("community"),
}
}
return nil
},
}
config := &settings.Configuration{
Runtime: &settings.Runtime{
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURCache: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *metadata.AURQuery) ([]*aur.Pkg, error) {
return []*aur.Pkg{}, nil
},
},
},
}
err = handleCmd(context.Background(), config, cmdArgs, db)
require.NoError(t, err)
require.Len(t, mockRunner.ShowCalls, len(wantShow))
require.Len(t, mockRunner.CaptureCalls, len(wantCapture))
for i, call := range mockRunner.ShowCalls {
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
show = strings.ReplaceAll(show, gitBin, "pacman")
// options are in a different order on different systems and on CI root user is used
assert.Subset(t, strings.Split(show, " "), strings.Split(wantShow[i], " "), fmt.Sprintf("%d - %s", i, show))
}
}
func TestIntegrationLocalInstallMissingDep(t *testing.T) {
wantErr := "could not find dotnet-sdk>=6"
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
gitBin := t.TempDir() + "/git"
tmpDir := t.TempDir()
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
tars := []string{
tmpDir + "/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
}
wantShow := []string{}
wantCapture := []string{}
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return strings.Join(tars, "\n"), "", nil
}
once := sync.Once{}
showOverride := func(cmd *exec.Cmd) error {
once.Do(func() {
for _, tar := range tars {
f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(t, err)
require.NoError(t, f.Close())
}
})
return nil
}
mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
PacmanConfigPath: "/etc/pacman.conf",
GitBin: "git",
Runner: mockRunner,
SudoLoopEnabled: false,
}
cmdArgs := parser.MakeArguments()
cmdArgs.AddArg("B")
cmdArgs.AddArg("i")
cmdArgs.AddTarget("testdata/jfin")
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "dotnet-sdk>=6", "dotnet-sdk<7", "dotnet-runtime>=6", "dotnet-runtime<7", "jellyfin-server=10.8.4", "jellyfin-web=10.8.4":
return false
}
return true
},
SyncSatisfierFn: func(s string) mock.IPackage {
switch s {
case "dotnet-runtime>=6", "dotnet-runtime<7":
return &mock.Package{
PName: "dotnet-runtime-6.0",
PBase: "dotnet-runtime-6.0",
PVersion: "6.0.100-1",
PDB: mock.NewDB("community"),
}
}
return nil
},
}
config := &settings.Configuration{
Runtime: &settings.Runtime{
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURCache: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *metadata.AURQuery) ([]*aur.Pkg, error) {
return []*aur.Pkg{}, nil
},
},
},
}
err = handleCmd(context.Background(), config, cmdArgs, db)
require.Error(t, err)
require.EqualError(t, err, wantErr)
require.Len(t, mockRunner.ShowCalls, len(wantShow))
require.Len(t, mockRunner.CaptureCalls, len(wantCapture))
for i, call := range mockRunner.ShowCalls {
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
show = strings.ReplaceAll(show, gitBin, "pacman")
// options are in a different order on different systems and on CI root user is used
assert.Subset(t, strings.Split(show, " "), strings.Split(wantShow[i], " "), fmt.Sprintf("%d - %s", i, show))
}
}

View File

@@ -141,7 +141,7 @@ func main() {
dbExecutor.Cleanup()
}()
if err = handleCmd(ctx, cmdArgs, db.Executor(dbExecutor)); err != nil {
if err = handleCmd(ctx, config, cmdArgs, db.Executor(dbExecutor)); err != nil {
if str := err.Error(); str != "" {
text.Errorln(str)
}

View File

@@ -74,7 +74,7 @@ func graphPackage(
fmt.Fprintln(os.Stdout, graph.String())
fmt.Fprintln(os.Stdout, "\nlayers\n", graph.TopoSortedLayers())
fmt.Fprintln(os.Stdout, "\ninverted order\n", graph.TopoSorted())
fmt.Fprintln(os.Stdout, "\nlayers map\n", graph.TopoSortedLayerMap())
fmt.Fprintln(os.Stdout, "\nlayers map\n", graph.TopoSortedLayerMap(nil))
return nil
}

View File

@@ -21,9 +21,13 @@ type DBExecutor struct {
PackagesFromGroupFn func(string) []IPackage
LocalSatisfierExistsFn func(string) bool
SyncSatisfierFn func(string) IPackage
AlpmArchitecturesFn func() ([]string, error)
}
func (t *DBExecutor) AlpmArchitectures() ([]string, error) {
if t.AlpmArchitecturesFn != nil {
return t.AlpmArchitecturesFn()
}
panic("implement me")
}

View File

@@ -300,7 +300,7 @@ func (g *Grapher) addNodes(
depType Reason,
) {
for _, depString := range deps {
depName, _, _ := splitDep(depString)
depName, mod, ver := splitDep(depString)
if g.dbExecutor.LocalSatisfierExists(depString) {
if g.fullGraph {
@@ -403,7 +403,16 @@ func (g *Grapher) addNodes(
}
// no dep found. add as missing
graph.SetNodeInfo(depString, &topo.NodeInfo[*InstallInfo]{Color: colorMap[depType], Background: bgColorMap[Missing]})
graph.AddNode(depName)
graph.SetNodeInfo(depName, &topo.NodeInfo[*InstallInfo]{
Color: colorMap[depType],
Background: bgColorMap[Missing],
Value: &InstallInfo{
Source: Missing,
Reason: depType,
Version: fmt.Sprintf("%s%s", mod, ver),
},
})
}
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/Jguer/yay/v11/pkg/db"
"github.com/Jguer/yay/v11/pkg/db/mock"
mockaur "github.com/Jguer/yay/v11/pkg/dep/mock"
aur "github.com/Jguer/yay/v11/pkg/query"
"github.com/Jguer/aur/metadata"
@@ -19,21 +20,7 @@ func ptrString(s string) *string {
return &s
}
type getFunc func(ctx context.Context, query *metadata.AURQuery) ([]*aur.Pkg, error)
type MockAUR struct {
GetFn getFunc
}
func (m *MockAUR) Get(ctx context.Context, query *metadata.AURQuery) ([]*aur.Pkg, error) {
if m.GetFn != nil {
return m.GetFn(ctx, query)
}
panic("implement me")
}
func getFromFile(t *testing.T, filePath string) getFunc {
func getFromFile(t *testing.T, filePath string) mockaur.GetFunc {
f, err := os.Open(filePath)
require.NoError(t, err)
@@ -85,7 +72,7 @@ func TestGrapher_GraphFromTargets_jellyfin(t *testing.T) {
},
}
mockAUR := &MockAUR{GetFn: func(ctx context.Context, query *metadata.AURQuery) ([]*aur.Pkg, error) {
mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *metadata.AURQuery) ([]*aur.Pkg, error) {
if query.Needles[0] == "jellyfin" {
jfinFn := getFromFile(t, "testdata/jellyfin.json")
return jfinFn(ctx, query)
@@ -210,7 +197,7 @@ func TestGrapher_GraphFromTargets_jellyfin(t *testing.T) {
tt.fields.noDeps, tt.fields.noCheckDeps)
got, err := g.GraphFromTargets(context.Background(), nil, tt.args.targets)
require.NoError(t, err)
layers := got.TopoSortedLayerMap()
layers := got.TopoSortedLayerMap(nil)
require.EqualValues(t, tt.want, layers, layers)
})
}

22
pkg/dep/mock/aur.go Normal file
View File

@@ -0,0 +1,22 @@
package mock
import (
"context"
"github.com/Jguer/aur"
"github.com/Jguer/aur/metadata"
)
type GetFunc func(ctx context.Context, query *metadata.AURQuery) ([]*aur.Pkg, error)
type MockAUR struct {
GetFn GetFunc
}
func (m *MockAUR) Get(ctx context.Context, query *metadata.AURQuery) ([]*aur.Pkg, error) {
if m.GetFn != nil {
return m.GetFn(ctx, query)
}
panic("implement me")
}

View File

@@ -2,6 +2,7 @@ package pgp
import (
"bytes"
"context"
"errors"
"fmt"
"os"
@@ -11,6 +12,7 @@ import (
gosrc "github.com/Morganamilo/go-srcinfo"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v11/pkg/settings/exe"
"github.com/Jguer/yay/v11/pkg/text"
)
@@ -41,17 +43,20 @@ func (set pgpKeySet) get(key string) bool {
return exists
}
type GPGCmdBuilder interface {
exe.Runner
BuildGPGCmd(ctx context.Context, extraArgs ...string) *exec.Cmd
}
// CheckPgpKeys iterates through the keys listed in the PKGBUILDs and if needed,
// asks the user whether yay should try to import them.
func CheckPgpKeys(pkgbuildDirsByBase map[string]string, srcinfos map[string]*gosrc.Srcinfo,
gpgBin, gpgFlags string, noConfirm bool,
func CheckPgpKeys(ctx context.Context, pkgbuildDirsByBase map[string]string, srcinfos map[string]*gosrc.Srcinfo,
cmdBuilder GPGCmdBuilder, noConfirm bool,
) ([]string, error) {
// Let's check the keys individually, and then we can offer to import
// the problematic ones.
problematic := make(pgpKeySet)
args := append(strings.Fields(gpgFlags), "--list-keys")
// Mapping all the keys.
for pkg := range pkgbuildDirsByBase {
srcinfo := srcinfos[pkg]
@@ -64,8 +69,7 @@ func CheckPgpKeys(pkgbuildDirsByBase map[string]string, srcinfos map[string]*gos
continue
}
cmd := exec.Command(gpgBin, append(args, key)...)
if err := cmd.Run(); err != nil {
if err := cmdBuilder.Show(cmdBuilder.BuildGPGCmd(ctx, "--list-keys", key)); err != nil {
problematic.set(key, pkg)
}
}
@@ -85,21 +89,17 @@ func CheckPgpKeys(pkgbuildDirsByBase map[string]string, srcinfos map[string]*gos
fmt.Println(str)
if text.ContinueTask(os.Stdin, gotext.Get("Import?"), true, noConfirm) {
return problematic.toSlice(), importKeys(problematic.toSlice(), gpgBin, gpgFlags)
return problematic.toSlice(), importKeys(ctx, cmdBuilder, problematic.toSlice())
}
return problematic.toSlice(), nil
}
// importKeys tries to import the list of keys specified in its argument.
func importKeys(keys []string, gpgBin, gpgFlags string) error {
args := append(strings.Fields(gpgFlags), "--recv-keys")
cmd := exec.Command(gpgBin, append(args, keys...)...)
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
func importKeys(ctx context.Context, cmdBuilder GPGCmdBuilder, keys []string) error {
text.OperationInfoln(gotext.Get("Importing keys with gpg..."))
if err := cmd.Run(); err != nil {
if err := cmdBuilder.Show(cmdBuilder.BuildGPGCmd(ctx, append([]string{"--recv-keys"}, keys...)...)); err != nil {
return errors.New(gotext.Get("problem importing keys"))
}

View File

@@ -1,134 +1,21 @@
package pgp
import (
"bytes"
"context"
"fmt"
"net/http"
"os"
"path"
"regexp"
"os/exec"
"sort"
"strings"
"testing"
"time"
gosrc "github.com/Morganamilo/go-srcinfo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v11/pkg/settings/exe"
)
const (
// The default port used by the PGP key server.
gpgServerPort = 11371
)
func init() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
regex := regexp.MustCompile(`search=0[xX]([a-fA-F0-9]+)`)
matches := regex.FindStringSubmatch(r.RequestURI)
data := ""
if matches != nil {
data = getPgpKey(matches[1])
}
w.Header().Set("Content-Type", "application/pgp-keys")
_, err := w.Write([]byte(data))
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
})
}
func getPgpKey(key string) string {
var buffer bytes.Buffer
if contents, err := os.ReadFile(path.Join("testdata", key)); err == nil {
buffer.WriteString("-----BEGIN PGP PUBLIC KEY BLOCK-----\n")
buffer.WriteString("Version: SKS 1.1.6\n")
buffer.WriteString("Comment: Hostname: yay\n\n")
buffer.Write(contents)
buffer.WriteString("\n-----END PGP PUBLIC KEY BLOCK-----\n")
}
return buffer.String()
}
func startPgpKeyServer() *http.Server {
srv := &http.Server{Addr: fmt.Sprintf("127.0.0.1:%d", gpgServerPort), ReadHeaderTimeout: 1 * time.Second}
go func() {
err := srv.ListenAndServe()
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
}()
return srv
}
func TestImportKeys(t *testing.T) {
keyringDir := t.TempDir()
server := startPgpKeyServer()
defer func() {
err := server.Shutdown(context.TODO())
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
}()
casetests := []struct {
keys []string
wantError bool
}{
// Single key, should succeed.
// C52048C0C0748FEE227D47A2702353E0F7E48EDB: Thomas Dickey.
{
keys: []string{"C52048C0C0748FEE227D47A2702353E0F7E48EDB"},
wantError: false,
},
// Two keys, should succeed as well.
// 11E521D646982372EB577A1F8F0871F202119294: Tom Stellard.
// B6C8F98282B944E3B0D5C2530FC3042E345AD05D: Hans Wennborg.
{
keys: []string{
"11E521D646982372EB577A1F8F0871F202119294",
"B6C8F98282B944E3B0D5C2530FC3042E345AD05D",
},
wantError: false,
},
// Single invalid key, should fail.
{
keys: []string{"THIS-SHOULD-FAIL"},
wantError: true,
},
// Two invalid keys, should fail.
{
keys: []string{"THIS-SHOULD-FAIL", "THIS-ONE-SHOULD-FAIL-TOO"},
wantError: true,
},
// Invalid + valid key. Should fail as well.
// 647F28654894E3BD457199BE38DBBDC86092693E: Greg Kroah-Hartman.
{
keys: []string{
"THIS-SHOULD-FAIL",
"647F28654894E3BD457199BE38DBBDC86092693E",
},
wantError: true,
},
}
for _, tt := range casetests {
err := importKeys(tt.keys, "gpg", fmt.Sprintf("--homedir %s --keyserver 127.0.0.1", keyringDir))
if !tt.wantError {
if err != nil {
t.Fatalf("Got error %q, want no error", err)
}
continue
}
// Here, we want to see the error.
if err == nil {
t.Fatalf("Got no error; want error")
}
}
}
func makeSrcinfo(pkgbase string, pgpkeys ...string) *gosrc.Srcinfo {
srcinfo := gosrc.Srcinfo{}
srcinfo.Pkgbase = pkgbase
@@ -138,22 +25,21 @@ func makeSrcinfo(pkgbase string, pgpkeys ...string) *gosrc.Srcinfo {
}
func TestCheckPgpKeys(t *testing.T) {
keyringDir := t.TempDir()
gpgBin := t.TempDir() + "/gpg"
server := startPgpKeyServer()
defer func() {
err := server.Shutdown(context.TODO())
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
}()
f, err := os.OpenFile(gpgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
casetests := []struct {
name string
pkgs map[string]string
srcinfos map[string]*gosrc.Srcinfo
wantError bool
expected []string
testcases := []struct {
name string
pkgs map[string]string
srcinfos map[string]*gosrc.Srcinfo
wantError bool
wantShow []string
wantCapture []string
showFn func(cmd *exec.Cmd) error
expected []string
}{
// cower: single package, one valid key not yet in the keyring.
// 487EACC08557AD082088DABA1EB2638FF56C0C53: Dave Reisner.
@@ -162,22 +48,44 @@ func TestCheckPgpKeys(t *testing.T) {
pkgs: map[string]string{"cower": ""},
srcinfos: map[string]*gosrc.Srcinfo{"cower": makeSrcinfo("cower", "487EACC08557AD082088DABA1EB2638FF56C0C53")},
wantError: false,
expected: []string{"487EACC08557AD082088DABA1EB2638FF56C0C53"},
wantShow: []string{
"gpg --homedir /tmp --list-keys 487EACC08557AD082088DABA1EB2638FF56C0C53",
"gpg --homedir /tmp --recv-keys 487EACC08557AD082088DABA1EB2638FF56C0C53",
},
wantCapture: []string{},
showFn: func(cmd *exec.Cmd) error {
s := cmd.String()
if strings.Contains(s, "--list-keys") {
return fmt.Errorf("key not found")
}
return nil
},
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: map[string]string{"libc++": ""},
srcinfos: map[string]*gosrc.Srcinfo{
"libc++": makeSrcinfo("libc++", "11E521D646982372EB577A1F8F0871F202119294", "B6C8F98282B944E3B0D5C2530FC3042E345AD05D"),
},
name: "two valid keys not yet in the keyring",
pkgs: map[string]string{"libc++": ""},
srcinfos: map[string]*gosrc.Srcinfo{"libc++": makeSrcinfo("libc++", "11E521D646982372EB577A1F8F0871F202119294", "B6C8F98282B944E3B0D5C2530FC3042E345AD05D")},
wantError: false,
expected: []string{"11E521D646982372EB577A1F8F0871F202119294", "B6C8F98282B944E3B0D5C2530FC3042E345AD05D"},
wantShow: []string{
"gpg --homedir /tmp --list-keys 11E521D646982372EB577A1F8F0871F202119294",
"gpg --homedir /tmp --list-keys B6C8F98282B944E3B0D5C2530FC3042E345AD05D",
"gpg --homedir /tmp --recv-keys 11E521D646982372EB577A1F8F0871F202119294 B6C8F98282B944E3B0D5C2530FC3042E345AD05D",
},
wantCapture: []string{},
showFn: func(cmd *exec.Cmd) error {
s := cmd.String()
if strings.Contains(s, "--list-keys") {
return fmt.Errorf("key not found")
}
return nil
},
expected: []string{"11E521D646982372EB577A1F8F0871F202119294", "B6C8F98282B944E3B0D5C2530FC3042E345AD05D"},
},
// Two dummy packages requiring the same key.
// ABAF11C65A2970B130ABE3C479BE3E4300411886: Linus Torvalds.
{
name: "Two dummy packages requiring the same key",
pkgs: map[string]string{"dummy-1": "", "dummy-2": ""},
@@ -186,8 +94,21 @@ func TestCheckPgpKeys(t *testing.T) {
"ABAF11C65A2970B130ABE3C479BE3E4300411886"),
"dummy-2": makeSrcinfo("dummy-2", "ABAF11C65A2970B130ABE3C479BE3E4300411886"),
},
wantError: false,
expected: []string{"ABAF11C65A2970B130ABE3C479BE3E4300411886"},
wantError: false,
expected: []string{"ABAF11C65A2970B130ABE3C479BE3E4300411886"},
wantCapture: []string{},
wantShow: []string{
"gpg --homedir /tmp --list-keys ABAF11C65A2970B130ABE3C479BE3E4300411886",
"gpg --homedir /tmp --recv-keys ABAF11C65A2970B130ABE3C479BE3E4300411886",
},
showFn: func(cmd *exec.Cmd) error {
s := cmd.String()
if strings.Contains(s, "--list-keys") {
return fmt.Errorf("key not found")
}
return nil
},
},
// dummy package: single package, two valid keys, one of them already
// in the keyring.
@@ -199,8 +120,23 @@ func TestCheckPgpKeys(t *testing.T) {
srcinfos: map[string]*gosrc.Srcinfo{
"dummy-3": makeSrcinfo("dummy-3", "11E521D646982372EB577A1F8F0871F202119294", "C52048C0C0748FEE227D47A2702353E0F7E48EDB"),
},
wantError: false,
expected: []string{"C52048C0C0748FEE227D47A2702353E0F7E48EDB"},
wantError: false,
expected: []string{"C52048C0C0748FEE227D47A2702353E0F7E48EDB"},
wantCapture: []string{},
showFn: func(cmd *exec.Cmd) error {
s := cmd.String()
if strings.Contains(s, "--list-keys") &&
!strings.Contains(s, "11E521D646982372EB577A1F8F0871F202119294") {
return fmt.Errorf("key not found")
}
return nil
},
wantShow: []string{
"gpg --homedir /tmp --list-keys 11E521D646982372EB577A1F8F0871F202119294",
"gpg --homedir /tmp --list-keys C52048C0C0748FEE227D47A2702353E0F7E48EDB",
"gpg --homedir /tmp --recv-keys C52048C0C0748FEE227D47A2702353E0F7E48EDB",
},
},
// Two dummy packages with existing keys.
{
@@ -210,32 +146,106 @@ func TestCheckPgpKeys(t *testing.T) {
"dummy-4": makeSrcinfo("dummy-4", "11E521D646982372EB577A1F8F0871F202119294"),
"dummy-5": makeSrcinfo("dummy-5", "C52048C0C0748FEE227D47A2702353E0F7E48EDB"),
},
wantError: false,
expected: []string{},
wantError: false,
expected: []string{},
wantCapture: []string{},
showFn: func(cmd *exec.Cmd) error {
return nil
},
wantShow: []string{
"gpg --homedir /tmp --list-keys 11E521D646982372EB577A1F8F0871F202119294",
"gpg --homedir /tmp --list-keys C52048C0C0748FEE227D47A2702353E0F7E48EDB",
},
},
// Dummy package with invalid key, should fail.
{
name: "one invalid",
pkgs: map[string]string{"dummy-7": ""},
srcinfos: map[string]*gosrc.Srcinfo{"dummy-7": makeSrcinfo("dummy-7", "THIS-SHOULD-FAIL")},
wantError: true,
name: "one invalid",
pkgs: map[string]string{"dummy-7": ""},
srcinfos: map[string]*gosrc.Srcinfo{"dummy-7": makeSrcinfo("dummy-7", "THIS-SHOULD-FAIL")},
wantError: true,
wantCapture: []string{},
wantShow: []string{
"gpg --homedir /tmp --list-keys THIS-SHOULD-FAIL",
"gpg --homedir /tmp --recv-keys THIS-SHOULD-FAIL",
},
showFn: func(cmd *exec.Cmd) error {
s := cmd.String()
if strings.Contains(s, "--list-keys") {
return fmt.Errorf("key not found")
}
if strings.Contains(s, "--recv-keys") {
return fmt.Errorf("invalid key")
}
return nil
},
},
// Dummy package with both an invalid an another valid key, should fail.
// A314827C4E4250A204CE6E13284FC34C8E4B1A25: Thomas Bächler.
{
name: "one invalid, one valid",
pkgs: map[string]string{"dummy-8": ""},
srcinfos: map[string]*gosrc.Srcinfo{"dummy-8": makeSrcinfo("dummy-8", "A314827C4E4250A204CE6E13284FC34C8E4B1A25", "THIS-SHOULD-FAIL")},
wantError: true,
expected: []string{},
name: "one invalid, one valid",
pkgs: map[string]string{"dummy-8": ""},
srcinfos: map[string]*gosrc.Srcinfo{"dummy-8": makeSrcinfo("dummy-8", "A314827C4E4250A204CE6E13284FC34C8E4B1A25", "THIS-SHOULD-FAIL")},
wantError: true,
expected: []string{},
wantCapture: []string{},
showFn: func(cmd *exec.Cmd) error {
s := cmd.String()
if strings.Contains(s, "--list-keys") {
return fmt.Errorf("key not found")
}
if strings.Contains(s, "--recv-keys") {
return fmt.Errorf("invalid key")
}
return nil
},
wantShow: []string{
"gpg --homedir /tmp --list-keys A314827C4E4250A204CE6E13284FC34C8E4B1A25",
"gpg --homedir /tmp --list-keys THIS-SHOULD-FAIL",
"gpg --homedir /tmp --recv-keys A314827C4E4250A204CE6E13284FC34C8E4B1A25 THIS-SHOULD-FAIL",
},
},
}
for _, tt := range casetests {
for _, tt := range testcases {
tt := tt
t.Run(tt.name, func(t *testing.T) {
problematic, err := CheckPgpKeys(tt.pkgs, tt.srcinfos, "gpg",
fmt.Sprintf("--homedir %s --keyserver 127.0.0.1", keyringDir), true)
mockRunner := &exe.MockRunner{
ShowFn: tt.showFn,
CaptureFn: func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return "", "", nil
},
}
cmdBuilder := exe.CmdBuilder{
GPGBin: gpgBin,
GPGFlags: []string{"--homedir /tmp"},
Runner: mockRunner,
}
problematic, err := CheckPgpKeys(context.Background(), tt.pkgs, tt.srcinfos, &cmdBuilder, true)
require.Len(t, mockRunner.ShowCalls, len(tt.wantShow))
require.Len(t, mockRunner.CaptureCalls, len(tt.wantCapture))
sort.SliceStable(mockRunner.ShowCalls, func(i, j int) bool {
return mockRunner.ShowCalls[i].Args[0].(*exec.Cmd).String() < mockRunner.ShowCalls[j].Args[0].(*exec.Cmd).String()
})
for i, call := range mockRunner.ShowCalls {
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, gpgBin, "gpg")
// options are in a different order on different systems and on CI root user is used
assert.Subset(t, strings.Split(show, " "), strings.Split(tt.wantShow[i], " "), show)
}
for i, call := range mockRunner.CaptureCalls {
capture := call.Args[0].(*exec.Cmd).String()
capture = strings.ReplaceAll(capture, gpgBin, "gpg")
assert.Subset(t, strings.Split(capture, " "), strings.Split(tt.wantCapture[i], " "), capture)
}
if tt.wantError {
require.Error(t, err)

View File

@@ -28,6 +28,10 @@ const (
Minimal
)
type AURCache interface {
Get(ctx context.Context, query *metadata.AURQuery) ([]*aur.Pkg, error)
}
type SourceQueryBuilder struct {
repoQuery
aurQuery
@@ -40,12 +44,12 @@ type SourceQueryBuilder struct {
singleLineResults bool
aurClient aur.ClientInterface
aurCache *metadata.Client
aurCache AURCache
}
func NewSourceQueryBuilder(
aurClient aur.ClientInterface,
aurCache *metadata.Client,
aurCache AURCache,
sortBy string,
targetMode parser.TargetMode,
searchBy string,
@@ -193,7 +197,7 @@ func filterAURResults(pkgS []string, results []aur.Pkg) []aur.Pkg {
// queryAUR searches AUR and narrows based on subarguments.
func queryAUR(ctx context.Context,
aurClient aur.ClientInterface, aurMetadata *metadata.Client,
aurClient aur.ClientInterface, aurMetadata AURCache,
pkgS []string, searchBy string, newEngine bool,
) ([]aur.Pkg, error) {
var (

View File

@@ -28,6 +28,7 @@ type GitCmdBuilder interface {
type ICmdBuilder interface {
Runner
BuildGitCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd
BuildGPGCmd(ctx context.Context, extraArgs ...string) *exec.Cmd
BuildMakepkgCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd
BuildPacmanCmd(ctx context.Context, args *parser.Arguments, mode parser.TargetMode, noConfirm bool) *exec.Cmd
AddMakepkgFlag(string)
@@ -38,6 +39,8 @@ type ICmdBuilder interface {
type CmdBuilder struct {
GitBin string
GitFlags []string
GPGBin string
GPGFlags []string
MakepkgFlags []string
MakepkgConfPath string
MakepkgBin string
@@ -50,6 +53,21 @@ type CmdBuilder struct {
Runner Runner
}
func (c *CmdBuilder) BuildGPGCmd(ctx context.Context, extraArgs ...string) *exec.Cmd {
args := make([]string, len(c.GPGFlags), len(c.GPGFlags)+len(extraArgs))
copy(args, c.GPGFlags)
if len(extraArgs) > 0 {
args = append(args, extraArgs...)
}
cmd := exec.CommandContext(ctx, c.GPGBin, args...)
cmd = c.deElevateCommand(ctx, cmd)
return cmd
}
func (c *CmdBuilder) BuildGitCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd {
args := make([]string, len(c.GitFlags), len(c.GitFlags)+len(extraArgs))
copy(args, c.GitFlags)

View File

@@ -2,6 +2,7 @@ package exe
import (
"context"
"fmt"
"os/exec"
"github.com/Jguer/yay/v11/pkg/settings/parser"
@@ -10,6 +11,11 @@ import (
type Call struct {
Res []interface{}
Args []interface{}
Dir string
}
func (c *Call) String() string {
return fmt.Sprintf("%+v", c.Args)
}
type MockBuilder struct {
@@ -84,6 +90,7 @@ func (m *MockRunner) Capture(cmd *exec.Cmd) (stdout, stderr string, err error) {
Args: []interface{}{
cmd,
},
Dir: cmd.Dir,
})
if m.CaptureFn != nil {
@@ -103,6 +110,7 @@ func (m *MockRunner) Show(cmd *exec.Cmd) error {
Args: []interface{}{
cmd,
},
Dir: cmd.Dir,
})
return err

View File

@@ -1,6 +1,7 @@
package settings
import (
"context"
"net/http"
"github.com/Jguer/yay/v11/pkg/db"
@@ -15,6 +16,10 @@ import (
"github.com/Morganamilo/go-pacmanconf"
)
type AURCache interface {
Get(ctx context.Context, query *metadata.AURQuery) ([]*aur.Pkg, error)
}
type Runtime struct {
Mode parser.TargetMode
QueryBuilder query.Builder
@@ -23,11 +28,11 @@ type Runtime struct {
CompletionPath string
ConfigPath string
PacmanConf *pacmanconf.Config
VCSStore *vcs.InfoStore
VCSStore vcs.Store
CmdBuilder exe.ICmdBuilder
HTTPClient *http.Client
AURClient *aur.Client
VoteClient *vote.Client
AURCache *metadata.Client
AURCache AURCache
DBExecutor db.Executor
}

View File

@@ -17,6 +17,8 @@ type NodeInfo[V any] struct {
Value V
}
type CheckFn[T comparable, V any] func(T, V) error
type Graph[T comparable, V any] struct {
alias AliasMap[T] // alias -> aliased
aliases DepMap[T] // aliased -> alias
@@ -234,7 +236,7 @@ func (g *Graph[T, V]) TopoSortedLayers() [][]T {
}
// TopoSortedLayerMap returns a slice of all of the graph nodes in topological sort order with their node info.
func (g *Graph[T, V]) TopoSortedLayerMap() []map[T]V {
func (g *Graph[T, V]) TopoSortedLayerMap(checkFn CheckFn[T, V]) []map[T]V {
layers := []map[T]V{}
// Copy the graph
@@ -249,6 +251,11 @@ func (g *Graph[T, V]) TopoSortedLayerMap() []map[T]V {
layers = append(layers, leaves)
for leafNode := range leaves {
if checkFn != nil {
if err := checkFn(leafNode, leaves[leafNode]); err != nil {
return nil
}
}
shrinkingGraph.remove(leafNode)
}
}

View File

@@ -26,31 +26,31 @@ type PreparerHookFunc func(ctx context.Context, config *settings.Configuration,
type Preparer struct {
dbExecutor db.Executor
cmdBuilder exe.ICmdBuilder
config *settings.Configuration
cfg *settings.Configuration
postDownloadHooks []PreparerHookFunc
postMergeHooks []PreparerHookFunc
makeDeps []string
}
func NewPreparer(dbExecutor db.Executor, cmdBuilder exe.ICmdBuilder, config *settings.Configuration) *Preparer {
func NewPreparer(dbExecutor db.Executor, cmdBuilder exe.ICmdBuilder, cfg *settings.Configuration) *Preparer {
preper := &Preparer{
dbExecutor: dbExecutor,
cmdBuilder: cmdBuilder,
config: config,
cfg: cfg,
postDownloadHooks: []PreparerHookFunc{},
postMergeHooks: []PreparerHookFunc{},
}
if config.CleanMenu {
if cfg.CleanMenu {
preper.postDownloadHooks = append(preper.postDownloadHooks, menus.CleanFn)
}
if config.DiffMenu {
if cfg.DiffMenu {
preper.postMergeHooks = append(preper.postMergeHooks, menus.DiffFn)
}
if config.EditMenu {
if cfg.EditMenu {
preper.postMergeHooks = append(preper.postMergeHooks, menus.EditFn)
}
@@ -58,14 +58,14 @@ func NewPreparer(dbExecutor db.Executor, cmdBuilder exe.ICmdBuilder, config *set
}
func (preper *Preparer) ShouldCleanAURDirs(pkgBuildDirs map[string]string) PostInstallHookFunc {
if !preper.config.CleanAfter || len(pkgBuildDirs) == 0 {
if !preper.cfg.CleanAfter || len(pkgBuildDirs) == 0 {
return nil
}
text.Debugln("added post install hook to clean up AUR dirs", pkgBuildDirs)
return func(ctx context.Context) error {
cleanAfter(ctx, preper.config.Runtime.CmdBuilder, pkgBuildDirs)
cleanAfter(ctx, preper.cfg.Runtime.CmdBuilder, pkgBuildDirs)
return nil
}
}
@@ -75,7 +75,7 @@ func (preper *Preparer) ShouldCleanMakeDeps() PostInstallHookFunc {
return nil
}
switch preper.config.RemoveMake {
switch preper.cfg.RemoveMake {
case "yes":
break
case "no":
@@ -89,7 +89,7 @@ func (preper *Preparer) ShouldCleanMakeDeps() PostInstallHookFunc {
text.Debugln("added post install hook to clean up AUR makedeps", preper.makeDeps)
return func(ctx context.Context) error {
return removeMake(ctx, preper.config.Runtime.CmdBuilder, preper.makeDeps)
return removeMake(ctx, preper.cfg.Runtime.CmdBuilder, preper.makeDeps)
}
}
@@ -152,7 +152,7 @@ func (preper *Preparer) PrepareWorkspace(ctx context.Context, targets []map[stri
for _, info := range layer {
if info.Source == dep.AUR {
pkgBase := *info.AURBase
pkgBuildDir := filepath.Join(preper.config.BuildDir, pkgBase)
pkgBuildDir := filepath.Join(preper.cfg.BuildDir, pkgBase)
if preper.needToCloneAURBase(info, pkgBuildDir) {
aurBasesToClone.Add(pkgBase)
}
@@ -166,27 +166,27 @@ func (preper *Preparer) PrepareWorkspace(ctx context.Context, targets []map[stri
if _, errA := download.AURPKGBUILDRepos(ctx,
preper.cmdBuilder, aurBasesToClone.ToSlice(),
config.AURURL, config.BuildDir, false); errA != nil {
preper.cfg.AURURL, preper.cfg.BuildDir, false); errA != nil {
return nil, errA
}
if errP := downloadPKGBUILDSourceFanout(ctx, config.Runtime.CmdBuilder,
pkgBuildDirsByBase, false, config.MaxConcurrentDownloads); errP != nil {
if errP := downloadPKGBUILDSourceFanout(ctx, preper.cmdBuilder,
pkgBuildDirsByBase, false, preper.cfg.MaxConcurrentDownloads); errP != nil {
text.Errorln(errP)
}
for _, hookFn := range preper.postDownloadHooks {
if err := hookFn(ctx, preper.config, os.Stdout, pkgBuildDirsByBase); err != nil {
if err := hookFn(ctx, preper.cfg, os.Stdout, pkgBuildDirsByBase); err != nil {
return nil, err
}
}
if err := mergePkgbuilds(ctx, pkgBuildDirsByBase); err != nil {
if err := mergePkgbuilds(ctx, preper.cmdBuilder, pkgBuildDirsByBase); err != nil {
return nil, err
}
for _, hookFn := range preper.postMergeHooks {
if err := hookFn(ctx, preper.config, os.Stdout, pkgBuildDirsByBase); err != nil {
if err := hookFn(ctx, preper.cfg, os.Stdout, pkgBuildDirsByBase); err != nil {
return nil, err
}
}
@@ -195,7 +195,7 @@ func (preper *Preparer) PrepareWorkspace(ctx context.Context, targets []map[stri
}
func (preper *Preparer) needToCloneAURBase(installInfo *dep.InstallInfo, pkgbuildDir string) bool {
if preper.config.ReDownload == "all" {
if preper.cfg.ReDownload == "all" {
return true
}

View File

@@ -1,18 +1,23 @@
package main
import (
"context"
"github.com/Jguer/yay/v11/pkg/db"
"github.com/Jguer/yay/v11/pkg/pgp"
"github.com/Jguer/yay/v11/pkg/settings"
"github.com/Jguer/yay/v11/pkg/settings/exe"
gosrc "github.com/Morganamilo/go-srcinfo"
)
type srcinfoOperator struct {
dbExecutor db.Executor
cfg *settings.Configuration
cmdBuilder exe.ICmdBuilder
}
func (s *srcinfoOperator) Run(pkgbuildDirs map[string]string) (map[string]*gosrc.Srcinfo, error) {
func (s *srcinfoOperator) Run(ctx context.Context, pkgbuildDirs map[string]string) (map[string]*gosrc.Srcinfo, error) {
srcinfos, err := parseSrcinfoFiles(pkgbuildDirs, true)
if err != nil {
return nil, err
@@ -22,8 +27,8 @@ func (s *srcinfoOperator) Run(pkgbuildDirs map[string]string) (map[string]*gosrc
return nil, err
}
if config.PGPFetch {
if _, errCPK := pgp.CheckPgpKeys(pkgbuildDirs, srcinfos, config.GpgBin, config.GpgFlags, settings.NoConfirm); errCPK != nil {
if s.cfg.PGPFetch {
if _, errCPK := pgp.CheckPgpKeys(ctx, pkgbuildDirs, srcinfos, s.cmdBuilder, settings.NoConfirm); errCPK != nil {
return nil, errCPK
}
}

28
sync.go
View File

@@ -2,6 +2,7 @@ package main
import (
"context"
"errors"
"fmt"
"os"
"strings"
@@ -59,7 +60,18 @@ func syncInstall(ctx context.Context,
}
opService := NewOperationService(ctx, config, dbExecutor)
return opService.Run(ctx, cmdArgs, graph.TopoSortedLayerMap())
multiErr := &multierror.MultiError{}
targets := graph.TopoSortedLayerMap(func(s string, ii *dep.InstallInfo) error {
if ii.Source == dep.Missing {
multiErr.Add(errors.New(gotext.Get("could not find %s%s", s, ii.Version)))
}
return nil
})
if err := multiErr.Return(); err != nil {
return err
}
return opService.Run(ctx, cmdArgs, targets)
}
type OperationService struct {
@@ -84,7 +96,7 @@ func (o *OperationService) Run(ctx context.Context,
fmt.Fprintln(os.Stdout, "", gotext.Get("there is nothing to do"))
return nil
}
preparer := NewPreparer(o.dbExecutor, config.Runtime.CmdBuilder, config)
preparer := NewPreparer(o.dbExecutor, o.cfg.Runtime.CmdBuilder, o.cfg)
installer := NewInstaller(o.dbExecutor, o.cfg.Runtime.CmdBuilder, o.cfg.Runtime.VCSStore, o.cfg.Runtime.Mode)
pkgBuildDirs, err := preparer.Run(ctx, os.Stdout, targets)
@@ -101,15 +113,19 @@ func (o *OperationService) Run(ctx context.Context,
installer.AddPostInstallHook(cleanAURDirsFunc)
}
srcinfoOp := srcinfoOperator{dbExecutor: o.dbExecutor}
srcinfos, err := srcinfoOp.Run(pkgBuildDirs)
srcinfoOp := srcinfoOperator{
dbExecutor: o.dbExecutor,
cfg: o.cfg,
cmdBuilder: installer.exeCmd,
}
srcinfos, err := srcinfoOp.Run(ctx, pkgBuildDirs)
if err != nil {
return err
}
go func() {
_ = completion.Update(ctx, config.Runtime.HTTPClient, o.dbExecutor,
config.AURURL, config.Runtime.CompletionPath, config.CompletionInterval, false)
_ = completion.Update(ctx, o.cfg.Runtime.HTTPClient, o.dbExecutor,
o.cfg.AURURL, o.cfg.Runtime.CompletionPath, o.cfg.CompletionInterval, false)
}()
err = installer.Install(ctx, cmdArgs, targets, pkgBuildDirs, srcinfos)

43
testdata/jfin/.SRCINFO vendored Normal file
View File

@@ -0,0 +1,43 @@
pkgbase = jellyfin
pkgdesc = The Free Software Media System
pkgver = 10.8.4
pkgrel = 1
url = https://github.com/jellyfin/jellyfin
arch = i686
arch = x86_64
arch = armv6h
license = GPL2
makedepends = dotnet-sdk>=6
makedepends = dotnet-sdk<7
makedepends = nodejs
makedepends = npm
makedepends = git
source = jellyfin-10.8.4.tar.gz::https://github.com/jellyfin/jellyfin/archive/v10.8.4.tar.gz
source = jellyfin-web-10.8.4.tar.gz::https://github.com/jellyfin/jellyfin-web/archive/v10.8.4.tar.gz
source = jellyfin.conf
source = jellyfin.service
source = jellyfin.sysusers
source = jellyfin.tmpfiles
sha512sums = cf472f36a759a7eb3724dac79d3bd2d6c9c58fc375293ad6eb8b5ce1ea1a8f6dd296cc36113b80b1c705a99eafb2bd9ffd9381fd52fa19aa12018d50656c9bde
sha512sums = 21983940689475de7f9d37a1016fb2dd740986ac27ffa2e0eac0bc9c84d68ac557fdc8afb64ca70b867af2d1e438293b98d5c155da402d3e985ab831042ba176
sha512sums = 2aa97a1a7a8a447171b59be3e93183e09cbbc32c816843cc47c6777b9aec48bd9c1d9d354f166e0b000ad8d2e94e6e4b0559aa52e5c159abbc103ed2c5afa3f0
sha512sums = 99d02080b1b92e731250f39ddd13ceca7129d69d0c05e0939620cbc3f499a9574668c63fa889704a4905560888131e980d7ab1fbcc5837b04d33ce26daa9d42b
sha512sums = 6fc2638e6ec4b1ee0240e17815c91107b694e5fde72c1bc7956c83067bbeacb632de899b86837e47a0ec04288131b15c20746373b45e0669c8976069a55d627a
sha512sums = 45a62b62d97b9a83289d4dfde684163b1bcf340c1921fb958e5a701812c61b392901841940c67e5fa5148783277d5b4dc65ba01d3a22e8f855ea62154ad9be33
pkgname = jellyfin
depends = jellyfin-web=10.8.4
depends = jellyfin-server=10.8.4
pkgname = jellyfin-web
pkgdesc = Jellyfin web client
pkgname = jellyfin-server
pkgdesc = Jellyfin server component
depends = dotnet-runtime>=6
depends = dotnet-runtime<7
depends = aspnet-runtime>=6
depends = aspnet-runtime<7
depends = ffmpeg
depends = sqlite
backup = etc/conf.d/jellyfin

View File

@@ -37,7 +37,7 @@ func filterUpdateList(list []db.Upgrade, filter upgrade.Filter) []db.Upgrade {
}
// upList returns lists of packages to upgrade from each source.
func upList(ctx context.Context, aurCache *metadata.Client,
func upList(ctx context.Context, aurCache settings.AURCache,
warnings *query.AURWarnings, dbExecutor db.Executor, enableDowngrade bool,
filter upgrade.Filter,
) (aurUp, repoUp upgrade.UpSlice, err error) {
@@ -262,7 +262,7 @@ func sysupgradeTargets(ctx context.Context, dbExecutor db.Executor,
// Targets for sys upgrade.
func sysupgradeTargetsV2(ctx context.Context,
aurCache *metadata.Client,
aurCache settings.AURCache,
dbExecutor db.Executor,
graph *topo.Graph[string, *dep.InstallInfo],
enableDowngrade bool,