Forráskód Böngészése

Add experimental vote util to yay (#1765)

* feat(vote): add vote utility

* update deps

* add vote and unvote capabilities

* use -W for web ops

* add fish completions

* add bash completion

* add zsh completion

* add md instructions
Jguer 2 éve
szülő
commit
0fdfe79943
12 módosított fájl, 152 hozzáadás és 17 törlés
  1. 3 0
      README.md
  2. 15 0
      cmd.go
  3. 7 3
      completions/bash
  4. 8 1
      completions/fish
  5. 11 0
      completions/zsh
  6. 17 2
      doc/yay.8
  7. 4 3
      go.mod
  8. 11 7
      go.sum
  9. 14 1
      pkg/settings/config.go
  10. 2 0
      pkg/settings/parser/parser.go
  11. 2 0
      pkg/settings/runtime.go
  12. 58 0
      vote.go

+ 3 - 0
README.md

@@ -19,6 +19,7 @@ Yet Another Yogurt - An AUR Helper Written in Go
 - Narrow search (`yay linux header` will first search `linux` and then narrow on `header`)
 - Find matching package providers during search and allow selection
 - Remove make dependencies at the end of the build process
+- Un/Vote for packages
 
 [![asciicast](https://asciinema.org/a/399431.svg)](https://asciinema.org/a/399431)
 
@@ -99,6 +100,8 @@ pacman -S --needed git base-devel yay
 | `yay -Y --gendb`                  | Generate development package database used for devel update.                                        |
 | `yay -Syu --devel`                | Perform system upgrade, but also check for development package updates.                             |
 | `yay -Syu --timeupdate`           | Perform system upgrade and use PKGBUILD modification time (not version number) to determine update. |
+| `yay -Wv <AUR Package>`           | Vote for package (Requires setting `AUR_USER` and `AUR_PASSWORD` environment variables). (yay v12+) |
+| `yay -Wu <AUR Package>`           | Unvote for package (Requires setting `AUR_USER` and `AUR_PASSWORD` environment variables) (yay v12+)|
 
 ## Frequently Asked Questions
 

+ 15 - 0
cmd.go

@@ -186,6 +186,8 @@ func handleCmd(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Exe
 		return handlePrint(ctx, cmdArgs, dbExecutor)
 	case "Y", "yay":
 		return handleYay(ctx, cmdArgs, dbExecutor, config.Runtime.QueryBuilder)
+	case "W", "web":
+		return handleWeb(ctx, cmdArgs)
 	}
 
 	return errors.New(gotext.Get("unhandled operation"))
@@ -304,6 +306,19 @@ func handleYay(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Exe
 	return nil
 }
 
+func handleWeb(ctx context.Context, cmdArgs *parser.Arguments) error {
+	switch {
+	case cmdArgs.ExistsArg("v", "vote"):
+		return handlePackageVote(ctx, cmdArgs.Targets, config.Runtime.AURClient,
+			config.Runtime.VoteClient, config.RequestSplitN, true)
+	case cmdArgs.ExistsArg("u", "unvote"):
+		return handlePackageVote(ctx, cmdArgs.Targets, config.Runtime.AURClient,
+			config.Runtime.VoteClient, config.RequestSplitN, false)
+	}
+
+	return nil
+}
+
 func handleGetpkgbuild(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor download.DBSearcher) error {
 	if cmdArgs.ExistsArg("p", "print") {
 		return printPkgbuilds(dbExecutor, config.Runtime.HTTPClient, cmdArgs.Targets, config.Runtime.Mode, config.AURURL)

+ 7 - 3
completions/bash

@@ -51,7 +51,7 @@ _pacman_repo_list() {
 _yay() {
   compopt -o default
   local common core cur database files prev query remove sync upgrade o
-  local yays show getpkgbuild
+  local yays show getpkgbuild web
   local cur prev words cword
 
   _init_completion || return
@@ -81,8 +81,9 @@ _yay() {
   yays=('clean gendb' 'c')
   show=('complete defaultconfig currentconfig stats news' 'c d g s w')
   getpkgbuild=('force print' 'f p')
+  web=('vote unvote' 'v u')
 
-  for o in 'D database' 'F files' 'Q query' 'R remove' 'S sync' 'U upgrade' 'Y yays' 'P show' 'G getpkgbuild'; do
+  for o in 'D database' 'F files' 'Q query' 'R remove' 'S sync' 'U upgrade' 'Y yays' 'P show' 'G getpkgbuild' 'W web'; do
     _arch_incomp "$o" && break
   done
 
@@ -119,6 +120,9 @@ _yay() {
       G)
         _yay_pkg
         ;;
+      W)
+        _yay_pkg
+        ;;
       esac
   fi
   true
@@ -126,7 +130,7 @@ _yay() {
 
 _pacman_file() {
   compopt -o filenames
-  _filedir 'pkg.tar*'
+  _filedir 'pkg.*'
 }
 
 complete -F _yay yay

+ 8 - 1
completions/fish

@@ -8,6 +8,7 @@ set -l progname yay
 set -l listall "(yay -Pc)"
 set -l listpacman "(__fish_print_packages)"
 set -l yayspecific '__fish_contains_opt -s Y yay'
+set -l webspecific '__fish_contains_opt -s W web'
 set -l show '__fish_contains_opt -s P show'
 set -l getpkgbuild '__fish_contains_opt -s G getpkgbuild'
 
@@ -16,7 +17,7 @@ set -l listinstalled "(pacman -Q | string replace ' ' \t)"
 set -l listrepos "(__fish_print_pacman_repos)"
 set -l listgroups "(pacman -Sg)\t'Package Group'"
 
-set -l noopt 'not __fish_contains_opt -s S -s D -s Q -s R -s U -s T -s F database query sync remove upgrade deptest files'
+set -l noopt 'not __fish_contains_opt -s S -s D -s Q -s R -s U -s T -s F -s Y -s W -s P -s G database query sync remove upgrade deptest files show getpkgbuild web yay'
 set -l database '__fish_contains_opt -s D database'
 set -l query '__fish_contains_opt -s Q query'
 set -l remove '__fish_contains_opt -s R remove'
@@ -156,6 +157,12 @@ complete -c $progname -n "$upgrade" -xa '(__fish_complete_suffix pkg.tar.zst; __
 complete -c $progname -s Y -f -l yay -n "$noopt" -d 'Yay specific operations'
 complete -c $progname -s P -f -l show -n "$noopt" -d 'Print information'
 complete -c $progname -s G -f -l getpkgbuild -n "$noopt" -d 'Get PKGBUILD from ABS or AUR'
+complete -c $progname -s W -f -l web -n "$noopt" -d 'Web operations'
+
+# Web options
+complete -c $progname -n "$webspecific" -s v -l vote -d 'Vote for AUR packages' -f
+complete -c $progname -n "$webspecific" -s u -l unvote -d 'Unvote for AUR packages' -f
+complete -c $progname -n "$webspecific" -xa "$listall"
 
 # New options
 complete -c $progname -n "not $noopt" -l repo -d 'Assume targets are from the AUR' -f

+ 11 - 0
completions/zsh

@@ -16,6 +16,7 @@ _pacman_opts_commands=(
 	{-T,--deptest}'[Check if dependencies are installed]'
 	{-U,--upgrade}'[Upgrade a package]'
 	{-Y,--yay}'[Yay specific options]'
+	{-W,--web}'[web options]'
 	{-V,--version}'[Display version and exit]'
 	'(-h --help)'{-h,--help}'[Display usage]'
 )
@@ -165,6 +166,12 @@ _pacman_opts_getpkgbuild_modifiers=(
 	{-p,--print}'[Print PKGBUILDs]:package:_pacman_completions_all_packages'
 )
 
+# -W
+_pacman_opts_web_modifiers=(
+	{-u,--unvote}'[Unvote AUR package]:package:_pacman_completions_all_packages'
+	{-v,--vote}'[Vote AUR package]:package:_pacman_completions_all_packages'
+)
+
 # -P
 _pacman_opts_print_modifiers=(
 		{-c,--complete}'[Used for completions]'
@@ -514,6 +521,10 @@ _pacman_zsh_comp() {
 			 _arguments -s : \
 				"$_pacman_opts_print_modifiers[@]"
 			;;
+		W*)
+			 _arguments -s : \
+				"$_pacman_opts_web_modifiers[@]"
+			;;
 		R*)
 			_pacman_action_remove
 			;;

+ 17 - 2
doc/yay.8

@@ -30,8 +30,7 @@ Perform yay specific print operations.
 
 .TP
 .B \-G, \-\-getpkgbuild
-Downloads PKGBUILD from ABS or AUR. The ABS can only be used for Arch Linux
-repositories
+Downloads PKGBUILD from ABS or AUR. The ABS can only be used for Arch Linux repositories.
 
 .RE
 If no arguments are provided 'yay \-Syu' will be performed.
@@ -137,6 +136,20 @@ ensures directories are not accidentally overwritten.
 .B \-p, \-\-print
 Prints the PKGBUILD of the given packages to stdout.
 
+.SH WEB OPTIONS (APPLY TO \-W AND \-\-WEB)
+
+.TP
+Web related operations such as voting for AUR packages.
+Requires setting AUR_USER and AUR_PASSWORD environment variables.
+
+.TP
+.B \-u, \-\-unvote
+Remove vote from AUR package(s)
+
+.TP
+.B \-v, \-\-vote
+Vote for AUR package(s)
+
 .SH PERMANENT CONFIGURATION SETTINGS
 .TP
 .B \-\-save
@@ -523,6 +536,8 @@ builds.
 .B \-\-nosudoloop
 Do not loop sudo calls in the background.
 
+.SH WEB OPTIONS (APPLY TO \-W AND \-\-WEB)
+
 .SH EXAMPLES
 .TP
 yay \fIfoo\fR

+ 4 - 3
go.mod

@@ -3,12 +3,13 @@ module github.com/Jguer/yay/v11
 require (
 	github.com/Jguer/aur v1.0.1
 	github.com/Jguer/go-alpm/v2 v2.1.2
+	github.com/Jguer/votar v1.0.0
 	github.com/Morganamilo/go-pacmanconf v0.0.0-20210502114700-cff030e927a5
 	github.com/Morganamilo/go-srcinfo v1.0.0
 	github.com/bradleyjkemp/cupaloy v2.3.0+incompatible
 	github.com/leonelquinteros/gotext v1.5.0
-	github.com/stretchr/testify v1.7.1
-	golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68
+	github.com/stretchr/testify v1.7.2
+	golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c
 	golang.org/x/term v0.0.0-20220526004731-065cf7ba2467
 	golang.org/x/text v0.3.7 // indirect
 	gopkg.in/h2non/gock.v1 v1.1.2
@@ -19,7 +20,7 @@ require (
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	gopkg.in/yaml.v3 v3.0.0-20220512140231-539c8e751b99 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
 
 go 1.17

+ 11 - 7
go.sum

@@ -2,12 +2,16 @@ github.com/Jguer/aur v1.0.1 h1:+GDOq0RuVn7CXpXzd8W85/+hPNDYonRZ3ONPm87e1jo=
 github.com/Jguer/aur v1.0.1/go.mod h1:1/SQjhWahmk2xKcmAm6XO1zGqK8HgYw3xlJM6a7845E=
 github.com/Jguer/go-alpm/v2 v2.1.2 h1:CGTIxzuEpT9Q3a7IBrx0E6acoYoaHX2Z93UOApPDhgU=
 github.com/Jguer/go-alpm/v2 v2.1.2/go.mod h1:uLQcTMNM904dRiGU+/JDtDdd7Nd8mVbEVaHjhmziT7w=
+github.com/Jguer/votar v1.0.0 h1:drPYpV5Py5BeAQS8xezmT6uCEfLzotNjLf5yfmlHKTg=
+github.com/Jguer/votar v1.0.0/go.mod h1:rc6vgVlTqNjI4nAnPbDTbdxw/N7kXkbB8BcUDjeFbYQ=
 github.com/Morganamilo/go-pacmanconf v0.0.0-20210502114700-cff030e927a5 h1:TMscPjkb1ThXN32LuFY5bEYIcXZx3YlwzhS1GxNpn/c=
 github.com/Morganamilo/go-pacmanconf v0.0.0-20210502114700-cff030e927a5/go.mod h1:Hk55m330jNiwxRodIlMCvw5iEyoRUCIY64W1p9D+tHc=
 github.com/Morganamilo/go-srcinfo v1.0.0 h1:Wh4nEF+HJWo+29hnxM18Q2hi+DUf0GejS13+Wg+dzmI=
 github.com/Morganamilo/go-srcinfo v1.0.0/go.mod h1:MP6VGY1NNpVUmYIEgoM9acix95KQqIRyqQ0hCLsyYUY=
 github.com/adrg/strutil v0.3.0 h1:bi/HB2zQbDihC8lxvATDTDzkT4bG7PATtVnDYp5rvq4=
 github.com/adrg/strutil v0.3.0/go.mod h1:Jz0wzBVE6Uiy9wxo62YEqEY1Nwto3QlLl1Il5gkLKWU=
+github.com/alexflint/go-arg v1.4.3/go.mod h1:3PZ/wp/8HuqRZMUUgu7I+e1qcpUbvmS258mRXkFH4IA=
+github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
 github.com/bradleyjkemp/cupaloy v2.3.0+incompatible h1:UafIjBvWQmS9i/xRg+CamMrnLTKNzo+bdmT/oH34c2Y=
 github.com/bradleyjkemp/cupaloy v2.3.0+incompatible/go.mod h1:Au1Xw1sgaJ5iSFktEhYsS0dbQiS1B0/XMXl+42y9Ilk=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -23,9 +27,11 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
+github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
@@ -35,12 +41,10 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a h1:N2T1jUrTQE9Re6TFF5PhvEHXHCguynGhKjWVsIUt5cY=
-golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 h1:z8Hj/bl9cOV2grsOpEaQFUaly0JWN3i97mo3jXKJNp0=
 golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8=
-golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU=
+golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM=
 golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -55,5 +59,5 @@ gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
 gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20220512140231-539c8e751b99 h1:dbuHpmKjkDzSOMKAWl10QNlgaZUd3V1q99xc81tt2Kc=
-gopkg.in/yaml.v3 v3.0.0-20220512140231-539c8e751b99/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 14 - 1
pkg/settings/config.go

@@ -14,6 +14,7 @@ import (
 	"github.com/leonelquinteros/gotext"
 
 	"github.com/Jguer/aur"
+	"github.com/Jguer/votar/pkg/vote"
 
 	"github.com/Jguer/yay/v11/pkg/settings/exe"
 	"github.com/Jguer/yay/v11/pkg/settings/parser"
@@ -252,6 +253,17 @@ func NewConfig(version string) (*Configuration, error) {
 		return nil, errPE
 	}
 
+	userAgent := fmt.Sprintf("Yay/%s", version)
+
+	voteClient, errVote := vote.NewClient(vote.WithUserAgent(userAgent))
+	if errVote != nil {
+		return nil, errVote
+	}
+
+	voteClient.SetCredentials(
+		os.Getenv("AUR_USERNAME"),
+		os.Getenv("AUR_PASSWORD"))
+
 	newConfig.Runtime = &Runtime{
 		ConfigPath:     configPath,
 		Version:        version,
@@ -263,13 +275,14 @@ func NewConfig(version string) (*Configuration, error) {
 		VCSStore:       nil,
 		HTTPClient:     &http.Client{},
 		AURClient:      nil,
+		VoteClient:     voteClient,
 	}
 
 	var errAUR error
 
 	newConfig.Runtime.AURClient, errAUR = aur.NewClient(aur.WithHTTPClient(newConfig.Runtime.HTTPClient),
 		aur.WithRequestEditorFn(func(ctx context.Context, req *http.Request) error {
-			req.Header.Set("User-Agent", fmt.Sprintf("Yay/%s", version))
+			req.Header.Set("User-Agent", userAgent)
 
 			return nil
 		}))

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

@@ -313,6 +313,7 @@ func isArg(arg string) bool {
 	case "V", "version":
 	case "h", "help":
 	case "Y", "yay":
+	case "W", "web":
 	case "P", "show":
 	case "G", "getpkgbuild":
 	case "b", "dbpath":
@@ -466,6 +467,7 @@ func isOp(op string) bool {
 	case "U", "upgrade":
 	// yay specific
 	case "Y", "yay":
+	case "W", "web":
 	case "P", "show":
 	case "G", "getpkgbuild":
 	default:

+ 2 - 0
pkg/settings/runtime.go

@@ -6,6 +6,7 @@ import (
 	"github.com/Morganamilo/go-pacmanconf"
 
 	"github.com/Jguer/aur"
+	"github.com/Jguer/votar/pkg/vote"
 
 	"github.com/Jguer/yay/v11/pkg/query"
 	"github.com/Jguer/yay/v11/pkg/settings/exe"
@@ -25,4 +26,5 @@ type Runtime struct {
 	CmdBuilder     exe.ICmdBuilder
 	HTTPClient     *http.Client
 	AURClient      *aur.Client
+	VoteClient     *vote.Client
 }

+ 58 - 0
vote.go

@@ -0,0 +1,58 @@
+package main
+
+import (
+	"context"
+	"errors"
+	"fmt"
+
+	"github.com/Jguer/aur"
+	"github.com/Jguer/votar/pkg/vote"
+	"github.com/leonelquinteros/gotext"
+
+	"github.com/Jguer/yay/v11/pkg/query"
+)
+
+type ErrAURVote struct {
+	inner   error
+	pkgName string
+}
+
+func (e *ErrAURVote) Error() string {
+	return gotext.Get("Unable to handle package vote for: %s. err: %s", e.pkgName, e.inner.Error())
+}
+
+func handlePackageVote(ctx context.Context,
+	targets []string, aurClient *aur.Client,
+	voteClient *vote.Client, splitN int, upvote bool,
+) error {
+	infos, err := query.AURInfoPrint(ctx, aurClient, targets, splitN)
+	if err != nil {
+		return err
+	}
+
+	if len(infos) == 0 {
+		fmt.Println(gotext.Get(" there is nothing to do"))
+		return nil
+	}
+
+	for _, info := range infos {
+		var err error
+		if upvote {
+			err = voteClient.Vote(ctx, info.PackageBase)
+		} else {
+			err = voteClient.Unvote(ctx, info.PackageBase)
+		}
+
+		if err != nil {
+			if errors.Is(err, vote.ErrNoCredentials) {
+				return errors.New(
+					gotext.Get("%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for voting",
+						err.Error()))
+			}
+
+			return &ErrAURVote{inner: err, pkgName: info.Name}
+		}
+	}
+
+	return nil
+}