Browse Source

feat(install): add --keepsrc to keep pkg/ and src/ directories (#2272)

* feat(install): add --nocleanbuild to keep pkg/ and src/ directories for
AUR packages

Providing this flag during installation of AUR packages allows for keeping
the src/ and pkg/ directories produced my makepkg. If the user wants to
delete the directories, they can either select to cleanBuild in the
cleanmenu or run the installation without the --nocleanbuild flag (yay
will only remove the directories if the package is rebuilt)

* fix(completion): simplify description for --nocleanbuild in fish

This makes the description consistent with the descriptions in the
man page, --help, and zsh completion.

* refactor(install): Rename --nocleanbuild to --keepsrc

This naming scheme is more familiar to users since it is the name of the
flag in Paru.

---------

Co-authored-by: jguer <me@jguer.space>
Joey Holtzman 1 year ago
parent
commit
04c76a404e

+ 1 - 0
cmd.go

@@ -102,6 +102,7 @@ Permanent configuration options:
 
     --cleanafter          Remove package sources after successful install
     --nocleanafter        Do not remove package sources after successful build
+    --keepsrc             Keep pkg/ and src/ after building packages
     --bottomup            Shows AUR's packages first and then repository's
     --topdown             Shows repository's packages first and then AUR's
     --singlelineresults   List each search result on its own line

+ 1 - 1
completions/bash

@@ -72,7 +72,7 @@ _yay() {
           makepkg pacman git gpg gpgflags config requestsplitn sudoloop nosudoloop
           redownload noredownload redownloadall rebuild rebuildall rebuildtree norebuild sortby
           singlelineresults doublelineresults answerclean answerdiff answeredit answerupgrade noanswerclean noanswerdiff
-          noansweredit noanswerupgrade cleanmenu diffmenu editmenu cleanafter nocleanafter
+          noansweredit noanswerupgrade cleanmenu diffmenu editmenu cleanafter nocleanafter keepsrc
           nocleanmenu nodiffmenu provides noprovides pgpfetch nopgpfetch
           useask nouseask combinedupgrade nocombinedupgrade aur repo makepkgconf
           nomakepkgconf askremovemake askyesremovemake removemake noremovemake completioninterval aururl aurrpcurl

+ 1 - 0
completions/fish

@@ -231,6 +231,7 @@ complete -c $progname -n "not $noopt" -l devel -d 'Check -git/-svn/-hg developme
 complete -c $progname -n "not $noopt" -l nodevel -d 'Disable development version checking' -f
 complete -c $progname -n "not $noopt" -l cleanafter -d 'Clean package sources after successful build' -f
 complete -c $progname -n "not $noopt" -l nocleanafter -d 'Disable package sources cleaning' -f
+complete -c $progname -n "not $noopt" -l keepsrc -d 'Keep pkg/ and src/ after building packages' -f
 complete -c $progname -n "not $noopt" -l timeupdate -d 'Check package modification date and version' -f
 complete -c $progname -n "not $noopt" -l notimeupdate -d 'Check only package version change' -f
 complete -c $progname -n "not $noopt" -l redownload -d 'Redownload PKGBUILD of package even if up-to-date' -f

+ 1 - 0
completions/zsh

@@ -86,6 +86,7 @@ _pacman_opts_common=(
 	'--nodevel[Disable development version checking]'
 	'--cleanafter[Clean package sources after successful build]'
 	'--nocleanafter[Disable package sources cleaning after successful build]'
+	'--keepsrc[Keep pkg/ and src/ after building packages]'
 	'--timeupdate[Check packages modification date and version]'
 	'--notimeupdate[Check only package version change]'
 	'--redownload[Always download pkgbuilds of targets]'

+ 4 - 0
doc/yay.8

@@ -385,6 +385,10 @@ instead of having to reclone the entire repo.
 Do not remove package sources after successful Install.
 
 .TP
+.B \-\-keepsrc
+Keep pkg/ and src/ after building packages
+
+.TP
 .B \-\-timeupdate
 During sysupgrade also compare the build time of installed packages against
 the last modification time of each package's AUR page.

+ 19 - 19
local_install_test.go

@@ -56,16 +56,16 @@ func TestIntegrationLocalInstall(t *testing.T) {
 	}
 
 	wantShow := []string{
-		"makepkg --verifysource --skippgpcheck -Ccf",
+		"makepkg --verifysource --skippgpcheck -f -Cc",
 		"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 --nobuild -f -C --ignorearch",
 		"makepkg -c --nobuild --noextract --ignorearch",
-		"makepkg --nobuild -fC --ignorearch",
+		"makepkg --nobuild -f -C --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-web-10.8.4-1-x86_64.pkg.tar.zst",
 		"pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin-server jellyfin-web",
-		"makepkg --nobuild -fC --ignorearch",
+		"makepkg --nobuild -f -C --ignorearch",
 		"makepkg -c --nobuild --noextract --ignorearch",
 		"pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
 		"pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin",
@@ -324,14 +324,14 @@ func TestIntegrationLocalInstallNeeded(t *testing.T) {
 	}
 
 	wantShow := []string{
-		"makepkg --verifysource --skippgpcheck -Ccf",
+		"makepkg --verifysource --skippgpcheck -f -Cc",
 		"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 --nobuild -f -C --ignorearch",
 		"makepkg -c --nobuild --noextract --ignorearch",
-		"makepkg --nobuild -fC --ignorearch",
+		"makepkg --nobuild -f -C --ignorearch",
 		"makepkg -c --nobuild --noextract --ignorearch",
-		"makepkg --nobuild -fC --ignorearch",
+		"makepkg --nobuild -f -C --ignorearch",
 		"makepkg -c --nobuild --noextract --ignorearch",
 	}
 
@@ -492,16 +492,16 @@ func TestIntegrationLocalInstallGenerateSRCINFO(t *testing.T) {
 	}
 
 	wantShow := []string{
-		"makepkg --verifysource --skippgpcheck -Ccf",
+		"makepkg --verifysource --skippgpcheck -f -Cc",
 		"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 --nobuild -f -C --ignorearch",
 		"makepkg -c --nobuild --noextract --ignorearch",
-		"makepkg --nobuild -fC --ignorearch",
+		"makepkg --nobuild -f -C --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-web-10.8.4-1-x86_64.pkg.tar.zst",
 		"pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin-server jellyfin-web",
-		"makepkg --nobuild -fC --ignorearch",
+		"makepkg --nobuild -f -C --ignorearch",
 		"makepkg -c --nobuild --noextract --ignorearch",
 		"pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
 		"pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin",
@@ -783,12 +783,12 @@ func TestIntegrationLocalInstallWithDepsProvides(t *testing.T) {
 	}
 
 	wantShow := []string{
-		"makepkg --verifysource --skippgpcheck -Ccf",
-		"makepkg --nobuild -fC --ignorearch",
+		"makepkg --verifysource --skippgpcheck -f -Cc",
+		"makepkg --nobuild -f -C --ignorearch",
 		"makepkg -c --nobuild --noextract --ignorearch",
 		"pacman -U --config /etc/pacman.conf -- /testdir/ceph-libs-bin-17.2.6-2-x86_64.pkg.tar.zst",
 		"pacman -D -q --asexplicit --config /etc/pacman.conf -- ceph-libs-bin",
-		"makepkg --nobuild -fC --ignorearch",
+		"makepkg --nobuild -f -C --ignorearch",
 		"makepkg -c --nobuild --noextract --ignorearch",
 		"pacman -U --config /etc/pacman.conf -- /testdir/ceph-bin-17.2.6-2-x86_64.pkg.tar.zst",
 		"pacman -D -q --asexplicit --config /etc/pacman.conf -- ceph-bin",
@@ -910,13 +910,13 @@ func TestIntegrationLocalInstallTwoSrcInfosWithDeps(t *testing.T) {
 	}
 
 	wantShow := []string{
-		"makepkg --verifysource --skippgpcheck -Ccf",
-		"makepkg --verifysource --skippgpcheck -Ccf",
-		"makepkg --nobuild -fC --ignorearch",
+		"makepkg --verifysource --skippgpcheck -f -Cc",
+		"makepkg --verifysource --skippgpcheck -f -Cc",
+		"makepkg --nobuild -f -C --ignorearch",
 		"makepkg -c --nobuild --noextract --ignorearch",
 		"pacman -U --config /etc/pacman.conf -- /testdir1/libzip-git-1.9.2.r166.gd2c47d0f-1-x86_64.pkg.tar.zst",
 		"pacman -D -q --asexplicit --config /etc/pacman.conf -- libzip-git",
-		"makepkg --nobuild -fC --ignorearch",
+		"makepkg --nobuild -f -C --ignorearch",
 		"makepkg -c --nobuild --noextract --ignorearch",
 		"pacman -U --config /etc/pacman.conf -- /testdir2/gourou-0.8.1-4-x86_64.pkg.tar.zst",
 		"pacman -D -q --asexplicit --config /etc/pacman.conf -- gourou",

+ 2 - 0
pkg/settings/args.go

@@ -53,6 +53,8 @@ func (c *Configuration) handleOption(option, value string) bool {
 		c.CleanAfter = true
 	case "noafterclean", "nocleanafter":
 		c.CleanAfter = false
+	case "keepsrc":
+		c.KeepSrc = true
 	case "debug":
 		c.Debug = true
 		return false

+ 2 - 0
pkg/settings/config.go

@@ -56,6 +56,7 @@ type Configuration struct {
 	TimeUpdate             bool   `json:"timeupdate"`
 	Devel                  bool   `json:"devel"`
 	CleanAfter             bool   `json:"cleanAfter"`
+	KeepSrc                bool   `json:"keepSrc"`
 	Provides               bool   `json:"provides"`
 	PGPFetch               bool   `json:"pgpfetch"`
 	CleanMenu              bool   `json:"cleanmenu"`
@@ -193,6 +194,7 @@ func DefaultConfig(version string) *Configuration {
 		AURURL:                 "https://aur.archlinux.org",
 		BuildDir:               os.ExpandEnv("$HOME/.cache/yay"),
 		CleanAfter:             false,
+		KeepSrc:                false,
 		Editor:                 "",
 		EditorFlags:            "",
 		Devel:                  false,

+ 7 - 0
pkg/settings/exe/cmd_builder.go

@@ -39,6 +39,7 @@ type ICmdBuilder interface {
 	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)
+	GetKeepSrc() bool
 	SudoLoop()
 }
 
@@ -56,6 +57,7 @@ type CmdBuilder struct {
 	PacmanBin        string
 	PacmanConfigPath string
 	PacmanDBPath     string
+	KeepSrc          bool
 	Runner           Runner
 	Log              *text.Logger
 }
@@ -75,6 +77,7 @@ func NewCmdBuilder(cfg *settings.Configuration, runner Runner, logger *text.Logg
 		PacmanBin:        cfg.PacmanBin,
 		PacmanConfigPath: cfg.PacmanConf,
 		PacmanDBPath:     dbPath,
+		KeepSrc:          cfg.KeepSrc,
 		Runner:           runner,
 		Log:              logger,
 	}
@@ -296,3 +299,7 @@ func (c *CmdBuilder) Show(cmd *exec.Cmd) error {
 func (c *CmdBuilder) Capture(cmd *exec.Cmd) (stdout, stderr string, err error) {
 	return c.Runner.Capture(cmd)
 }
+
+func (c *CmdBuilder) GetKeepSrc() bool {
+	return c.KeepSrc
+}

+ 5 - 0
pkg/settings/exe/mock.go

@@ -25,6 +25,7 @@ type MockBuilder struct {
 	BuildMakepkgCmdCalls   []Call
 	BuildMakepkgCmdFn      func(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd
 	BuildPacmanCmdFn       func(ctx context.Context, args *parser.Arguments, mode parser.TargetMode, noConfirm bool) *exec.Cmd
+	GetKeepSrcFn           func() bool
 }
 
 type MockRunner struct {
@@ -95,6 +96,10 @@ func (m *MockBuilder) Show(cmd *exec.Cmd) error {
 	return m.Runner.Show(cmd)
 }
 
+func (m *MockBuilder) GetKeepSrc() bool {
+	return false
+}
+
 func (m *MockRunner) Capture(cmd *exec.Cmd) (stdout, stderr string, err error) {
 	m.CaptureCallsMu.Lock()
 	m.CaptureCalls = append(m.CaptureCalls, Call{

+ 1 - 0
pkg/settings/parser/parser.go

@@ -378,6 +378,7 @@ func isArg(arg string) bool {
 	case "save":
 	case "afterclean", "cleanafter":
 	case "noafterclean", "nocleanafter":
+	case "keepsrc":
 	case "devel":
 	case "nodevel":
 	case "timeupdate":

+ 12 - 4
pkg/sync/build/installer.go

@@ -277,7 +277,11 @@ func (installer *Installer) buildPkg(ctx context.Context,
 	dir, base string,
 	installIncompatible, needed, isTarget bool,
 ) (map[string]string, error) {
-	args := []string{"--nobuild", "-fC"}
+	args := []string{"--nobuild", "-f"}
+
+	if !installer.exeCmd.GetKeepSrc() {
+		args = append(args, "-C")
+	}
 
 	if installIncompatible {
 		args = append(args, "--ignorearch")
@@ -296,19 +300,23 @@ func (installer *Installer) buildPkg(ctx context.Context,
 
 	switch {
 	case needed && installer.pkgsAreAlreadyInstalled(pkgdests, pkgVersion) || installer.downloadOnly:
-		args = []string{"-c", "--nobuild", "--noextract", "--ignorearch"}
+		args = []string{"--nobuild", "--noextract", "--ignorearch"}
 		pkgdests = map[string]string{}
 		installer.log.Warnln(gotext.Get("%s is up to date -- skipping", text.Cyan(base+"-"+pkgVersion)))
 	case installer.skipAlreadyBuiltPkg(isTarget, pkgdests):
-		args = []string{"-c", "--nobuild", "--noextract", "--ignorearch"}
+		args = []string{"--nobuild", "--noextract", "--ignorearch"}
 		installer.log.Warnln(gotext.Get("%s already made -- skipping build", text.Cyan(base+"-"+pkgVersion)))
 	default:
-		args = []string{"-cf", "--noconfirm", "--noextract", "--noprepare", "--holdver"}
+		args = []string{"-f", "--noconfirm", "--noextract", "--noprepare", "--holdver"}
 		if installIncompatible {
 			args = append(args, "--ignorearch")
 		}
 	}
 
+	if !installer.exeCmd.GetKeepSrc() {
+		args = append(args, "-c")
+	}
+
 	errMake := installer.exeCmd.Show(
 		installer.exeCmd.BuildMakepkgCmd(ctx,
 			dir, args...))

+ 137 - 33
pkg/sync/build/installer_test.go

@@ -56,8 +56,8 @@ func TestInstaller_InstallNeeded(t *testing.T) {
 			isInstalled: false,
 			isBuilt:     false,
 			wantShow: []string{
-				"makepkg --nobuild -fC --ignorearch",
-				"makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
+				"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
 				"pacman -U --needed --config  -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
 				"pacman -D -q --asexplicit --config  -- yay",
 			},
@@ -68,7 +68,7 @@ func TestInstaller_InstallNeeded(t *testing.T) {
 			isInstalled: false,
 			isBuilt:     true,
 			wantShow: []string{
-				"makepkg --nobuild -fC --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
 				"makepkg -c --nobuild --noextract --ignorearch",
 				"pacman -U --needed --config  -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
 				"pacman -D -q --asexplicit --config  -- yay",
@@ -80,7 +80,7 @@ func TestInstaller_InstallNeeded(t *testing.T) {
 			isInstalled: true,
 			isBuilt:     false,
 			wantShow: []string{
-				"makepkg --nobuild -fC --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
 				"makepkg -c --nobuild --noextract --ignorearch",
 			},
 			wantCapture: []string{"makepkg --packagelist"},
@@ -212,8 +212,8 @@ func TestInstaller_InstallMixedSourcesAndLayers(t *testing.T) {
 			wantShow: []string{
 				"pacman -S --config /etc/pacman.conf -- core/linux",
 				"pacman -D -q --asdeps --config /etc/pacman.conf -- linux",
-				"makepkg --nobuild -fC --ignorearch",
-				"makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
+				"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
 				"pacman -U --config /etc/pacman.conf -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
 				"pacman -D -q --asexplicit --config /etc/pacman.conf -- yay",
 			},
@@ -241,8 +241,8 @@ func TestInstaller_InstallMixedSourcesAndLayers(t *testing.T) {
 			wantShow: []string{
 				"pacman -S --config /etc/pacman.conf -- core/linux",
 				"pacman -D -q --asdeps --config /etc/pacman.conf -- linux",
-				"makepkg --nobuild -fC --ignorearch",
-				"makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
+				"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
 				"pacman -U --config /etc/pacman.conf -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
 				"pacman -D -q --asexplicit --config /etc/pacman.conf -- yay",
 			},
@@ -293,10 +293,10 @@ func TestInstaller_InstallMixedSourcesAndLayers(t *testing.T) {
 		{
 			desc: "same layer -- aur",
 			wantShow: []string{
-				"makepkg --nobuild -fC --ignorearch",
-				"makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
-				"makepkg --nobuild -fC --ignorearch",
-				"makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
+				"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
+				"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
 				"pacman -U --config /etc/pacman.conf -- pacman -U --config /etc/pacman.conf -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
 				"pacman -D -q --asexplicit --config /etc/pacman.conf -- yay",
 			},
@@ -323,12 +323,12 @@ func TestInstaller_InstallMixedSourcesAndLayers(t *testing.T) {
 		{
 			desc: "different layer -- aur",
 			wantShow: []string{
-				"makepkg --nobuild -fC --ignorearch",
-				"makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
+				"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
 				"pacman -U --config /etc/pacman.conf -- pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-server-10.8.8-1-x86_64.pkg.tar.zst",
 				"pacman -D -q --asdeps --config /etc/pacman.conf -- jellyfin-server",
-				"makepkg --nobuild -fC --ignorearch",
-				"makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
+				"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
 				"pacman -U --config /etc/pacman.conf -- pacman -U --config /etc/pacman.conf -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
 				"pacman -D -q --asexplicit --config /etc/pacman.conf -- yay",
 			},
@@ -374,13 +374,13 @@ func TestInstaller_InstallMixedSourcesAndLayers(t *testing.T) {
 			}
 
 			showOverride := func(cmd *exec.Cmd) error {
-				if strings.Contains(cmd.String(), "makepkg -cf --noconfirm") && cmd.Dir == tmpDir {
+				if strings.Contains(cmd.String(), "makepkg -f --noconfirm") && cmd.Dir == tmpDir {
 					f, err := os.OpenFile(pkgTar, os.O_RDONLY|os.O_CREATE, 0o666)
 					require.NoError(td, err)
 					require.NoError(td, f.Close())
 				}
 
-				if strings.Contains(cmd.String(), "makepkg -cf --noconfirm") && cmd.Dir == tmpDirJfin {
+				if strings.Contains(cmd.String(), "makepkg -f --noconfirm") && cmd.Dir == tmpDirJfin {
 					f, err := os.OpenFile(jfinPkgTar, os.O_RDONLY|os.O_CREATE, 0o666)
 					require.NoError(td, err)
 					require.NoError(td, f.Close())
@@ -570,7 +570,7 @@ func TestInstaller_CompileFailed(t *testing.T) {
 			}
 
 			showOverride := func(cmd *exec.Cmd) error {
-				if tc.failBuild && strings.Contains(cmd.String(), "makepkg -cf --noconfirm") && cmd.Dir == tmpDir {
+				if tc.failBuild && strings.Contains(cmd.String(), "makepkg -f --noconfirm") && cmd.Dir == tmpDir {
 					return errors.New("makepkg failed")
 				}
 				return nil
@@ -694,13 +694,13 @@ func TestInstaller_InstallSplitPackage(t *testing.T) {
 			wantShow: []string{
 				"pacman -S --config /etc/pacman.conf -- community/dotnet-runtime-6.0 community/aspnet-runtime community/dotnet-sdk-6.0",
 				"pacman -D -q --asdeps --config /etc/pacman.conf -- dotnet-runtime-6.0 aspnet-runtime dotnet-sdk-6.0",
-				"makepkg --nobuild -fC --ignorearch",
-				"makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
-				"makepkg --nobuild -fC --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
+				"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
 				"makepkg -c --nobuild --noextract --ignorearch",
 				"pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst /testdir/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
 				"pacman -D -q --asdeps --config /etc/pacman.conf -- jellyfin-server jellyfin-web",
-				"makepkg --nobuild -fC --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
 				"makepkg -c --nobuild --noextract --ignorearch",
 				"pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
 				"pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin",
@@ -818,7 +818,7 @@ func TestInstaller_InstallDownloadOnly(t *testing.T) {
 			isInstalled: false,
 			isBuilt:     false,
 			wantShow: []string{
-				"makepkg --nobuild -fC --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
 				"makepkg -c --nobuild --noextract --ignorearch",
 			},
 			wantCapture: []string{"makepkg --packagelist"},
@@ -828,7 +828,7 @@ func TestInstaller_InstallDownloadOnly(t *testing.T) {
 			isInstalled: false,
 			isBuilt:     true,
 			wantShow: []string{
-				"makepkg --nobuild -fC --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
 				"makepkg -c --nobuild --noextract --ignorearch",
 			},
 			wantCapture: []string{"makepkg --packagelist"},
@@ -838,7 +838,7 @@ func TestInstaller_InstallDownloadOnly(t *testing.T) {
 			isInstalled: true,
 			isBuilt:     false,
 			wantShow: []string{
-				"makepkg --nobuild -fC --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
 				"makepkg -c --nobuild --noextract --ignorearch",
 			},
 			wantCapture: []string{"makepkg --packagelist"},
@@ -1075,7 +1075,7 @@ func TestInstaller_InstallRebuild(t *testing.T) {
 			isBuilt:       true,
 			isInstalled:   false,
 			wantShow: []string{
-				"makepkg --nobuild -fC --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
 				"makepkg -c --nobuild --noextract --ignorearch",
 				"pacman -U --config  -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
 				"pacman -D -q --asexplicit --config  -- yay",
@@ -1099,8 +1099,8 @@ func TestInstaller_InstallRebuild(t *testing.T) {
 			isBuilt:       true,
 			isInstalled:   false,
 			wantShow: []string{
-				"makepkg --nobuild -fC --ignorearch",
-				"makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
+				"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
 				"pacman -U --config  -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
 				"pacman -D -q --asexplicit --config  -- yay",
 			},
@@ -1123,8 +1123,8 @@ func TestInstaller_InstallRebuild(t *testing.T) {
 			isInstalled:   true,
 			isBuilt:       true,
 			wantShow: []string{
-				"makepkg --nobuild -fC --ignorearch",
-				"makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
+				"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
 				"pacman -U --config  -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
 				"pacman -D -q --asexplicit --config  -- yay",
 			},
@@ -1147,8 +1147,8 @@ func TestInstaller_InstallRebuild(t *testing.T) {
 			isInstalled:   true,
 			isBuilt:       true,
 			wantShow: []string{
-				"makepkg --nobuild -fC --ignorearch",
-				"makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
+				"makepkg --nobuild -f -C --ignorearch",
+				"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
 				"pacman -U --config  -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
 				"pacman -D -q --asdeps --config  -- yay",
 			},
@@ -1337,3 +1337,107 @@ func TestInstaller_InstallUpgrade(t *testing.T) {
 		})
 	}
 }
+
+func TestInstaller_KeepSrc(t *testing.T) {
+	t.Parallel()
+
+	makepkgBin := t.TempDir() + "/makepkg"
+	pacmanBin := t.TempDir() + "/pacman"
+	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())
+
+	type testCase struct {
+		desc     string
+		wantShow []string
+		targets  []map[string]*dep.InstallInfo
+	}
+
+	tmpDir := t.TempDir()
+
+	testCases := []testCase{
+		{
+			desc: "--keepsrc",
+			wantShow: []string{
+				"makepkg --nobuild -f --ignorearch",
+				"makepkg --nobuild --noextract --ignorearch",
+				"pacman -U --config  -- /testdir/yay-92.0.0-1-x86_64.pkg.tar.zst",
+				"pacman -D -q --asexplicit --config  -- yay",
+			},
+			targets: []map[string]*dep.InstallInfo{
+				{
+					"yay": {
+						Source:      dep.AUR,
+						Reason:      dep.Explicit,
+						Version:     "92.0.0-1",
+						SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
+						AURBase:     ptrString("yay"),
+					},
+				},
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		tc := tc
+		t.Run(tc.desc, func(td *testing.T) {
+			tmpDir := td.TempDir()
+			pkgTar := tmpDir + "/yay-92.0.0-1-x86_64.pkg.tar.zst"
+
+			captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
+				return pkgTar, "", nil
+			}
+
+			// create a mock file
+			f, err := os.OpenFile(pkgTar, os.O_RDONLY|os.O_CREATE, 0o666)
+			require.NoError(td, err)
+			require.NoError(td, f.Close())
+
+			mockDB := &mock.DBExecutor{}
+			mockRunner := &exe.MockRunner{CaptureFn: captureOverride}
+			cmdBuilder := &exe.CmdBuilder{
+				MakepkgBin:      makepkgBin,
+				SudoBin:         "su",
+				PacmanBin:       pacmanBin,
+				KeepSrc:         true,
+				Runner:          mockRunner,
+				SudoLoopEnabled: false,
+			}
+
+			installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny,
+				parser.RebuildModeNo, false, newTestLogger())
+
+			cmdArgs := parser.MakeArguments()
+			cmdArgs.AddTarget("yay")
+
+			pkgBuildDirs := map[string]string{
+				"yay": tmpDir,
+			}
+
+			errI := installer.Install(context.Background(), cmdArgs, tc.targets, pkgBuildDirs, []string{}, false)
+			require.NoError(td, errI)
+
+			require.Len(td, mockRunner.ShowCalls, len(tc.wantShow))
+
+			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")
+
+				// options are in a different order on different systems and on CI root user is used
+				assert.Subset(td, strings.Split(show, " "), strings.Split(tc.wantShow[i], " "), show)
+
+				// Only assert makepkg commands don't have clean arguments
+				if strings.HasPrefix(show, "makepkg") {
+					assert.NotContains(td, show, "-c")
+					assert.NotContains(td, show, "-C")
+				}
+			}
+		})
+	}
+}

+ 5 - 1
pkg/sync/workdir/aur_source.go

@@ -32,7 +32,11 @@ func (e *ErrDownloadSource) Unwrap() error {
 func downloadPKGBUILDSource(ctx context.Context,
 	cmdBuilder exe.ICmdBuilder, pkgBuildDir string, installIncompatible bool,
 ) error {
-	args := []string{"--verifysource", "--skippgpcheck", "-Ccf"}
+	args := []string{"--verifysource", "--skippgpcheck", "-f"}
+
+	if !cmdBuilder.GetKeepSrc() {
+		args = append(args, "-Cc")
+	}
 
 	if installIncompatible {
 		args = append(args, "--ignorearch")

+ 47 - 9
pkg/sync/workdir/aur_source_test.go

@@ -33,6 +33,10 @@ func (z *TestMakepkgBuilder) BuildMakepkgCmd(ctx context.Context, dir string, ex
 		assert.Contains(z.test, cmd.String(), z.want)
 	}
 
+	if z.GetKeepSrc() {
+		assert.NotContains(z.test, cmd.String(), "-Cc")
+	}
+
 	if z.wantDir != "" {
 		assert.Equal(z.test, z.wantDir, cmd.Dir)
 	}
@@ -46,20 +50,54 @@ func (z *TestMakepkgBuilder) Show(cmd *exec.Cmd) error {
 	return z.showError
 }
 
+func (z *TestMakepkgBuilder) GetKeepSrc() bool {
+	return z.parentBuilder.KeepSrc
+}
+
 // GIVEN 1 package
 // WHEN downloadPKGBUILDSource is called
 // THEN 1 call should be made to makepkg with the specified parameters and dir
 func Test_downloadPKGBUILDSource(t *testing.T) {
 	t.Parallel()
-	cmdBuilder := &TestMakepkgBuilder{
-		parentBuilder: &exe.CmdBuilder{MakepkgConfPath: "/etc/not.conf", MakepkgFlags: []string{"--nocheck"}, MakepkgBin: "makepkg"},
-		test:          t,
-		want:          "makepkg --nocheck --config /etc/not.conf --verifysource --skippgpcheck -Ccf",
-		wantDir:       "/tmp/yay-bin",
+
+	type testCase struct {
+		desc    string
+		keepSrc bool
+		want    string
+	}
+
+	testCases := []testCase{
+		{
+			desc:    "keepsrc",
+			keepSrc: true,
+			want:    "makepkg --nocheck --config /etc/not.conf --verifysource --skippgpcheck -f",
+		},
+		{
+			desc:    "nokeepsrc",
+			keepSrc: false,
+			want:    "makepkg --nocheck --config /etc/not.conf --verifysource --skippgpcheck -f -Cc",
+		},
+	}
+
+	for _, tc := range testCases {
+		tc := tc
+		t.Run(tc.desc, func(td *testing.T) {
+			cmdBuilder := &TestMakepkgBuilder{
+				parentBuilder: &exe.CmdBuilder{
+					MakepkgConfPath: "/etc/not.conf",
+					MakepkgFlags:    []string{"--nocheck"},
+					MakepkgBin:      "makepkg",
+					KeepSrc:         tc.keepSrc,
+				},
+				test:    t,
+				want:    tc.want,
+				wantDir: "/tmp/yay-bin",
+			}
+			err := downloadPKGBUILDSource(context.Background(), cmdBuilder, filepath.Join("/tmp", "yay-bin"), false)
+			assert.NoError(t, err)
+			assert.Equal(t, 1, int(cmdBuilder.passes))
+		})
 	}
-	err := downloadPKGBUILDSource(context.Background(), cmdBuilder, filepath.Join("/tmp", "yay-bin"), false)
-	assert.NoError(t, err)
-	assert.Equal(t, 1, int(cmdBuilder.passes))
 }
 
 // GIVEN 1 package
@@ -70,7 +108,7 @@ func Test_downloadPKGBUILDSourceError(t *testing.T) {
 	cmdBuilder := &TestMakepkgBuilder{
 		parentBuilder: &exe.CmdBuilder{MakepkgConfPath: "/etc/not.conf", MakepkgFlags: []string{"--nocheck"}, MakepkgBin: "makepkg"},
 		test:          t,
-		want:          "makepkg --nocheck --config /etc/not.conf --verifysource --skippgpcheck -Ccf",
+		want:          "makepkg --nocheck --config /etc/not.conf --verifysource --skippgpcheck -f -Cc",
 		wantDir:       "/tmp/yay-bin",
 		showError:     &exec.ExitError{},
 	}

+ 3 - 3
sync_test.go

@@ -552,11 +552,11 @@ pkgname = python-vosk
 	}
 	wantShow := []string{
 		"pacman -S -y --config /etc/pacman.conf --",
-		"makepkg --verifysource --skippgpcheck -Ccf", "makepkg --nobuild -fC --ignorearch",
+		"makepkg --verifysource --skippgpcheck -f -Cc", "makepkg --nobuild -f -C --ignorearch",
 		"makepkg -c --nobuild --noextract --ignorearch",
 		"pacman -U --config /etc/pacman.conf -- /testdir/vosk-api-0.3.45-1-x86_64.pkg.tar.zst",
-		"makepkg --nobuild -fC --ignorearch", "makepkg -c --nobuild --noextract --ignorearch",
-		"makepkg --nobuild -fC --ignorearch", "makepkg -c --nobuild --noextract --ignorearch",
+		"makepkg --nobuild -f -C --ignorearch", "makepkg -c --nobuild --noextract --ignorearch",
+		"makepkg --nobuild -f -C --ignorearch", "makepkg -c --nobuild --noextract --ignorearch",
 		"pacman -U --config /etc/pacman.conf -- /testdir/vosk-api-0.3.45-1-x86_64.pkg.tar.zst /testdir/python-vosk-0.3.45-1-x86_64.pkg.tar.zst",
 		"pacman -D -q --asdeps --config /etc/pacman.conf -- vosk-api",
 		"pacman -D -q --asexplicit --config /etc/pacman.conf -- python-vosk",