diff --git a/README.md b/README.md index 99abacd3..a54c7baa 100644 --- a/README.md +++ b/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 ` | Vote for package (Requires setting `AUR_USER` and `AUR_PASSWORD` environment variables). (yay v12+) | +| `yay -Wu ` | Unvote for package (Requires setting `AUR_USER` and `AUR_PASSWORD` environment variables) (yay v12+)| ## Frequently Asked Questions diff --git a/cmd.go b/cmd.go index d830c0bd..b1e70fd6 100644 --- a/cmd.go +++ b/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) diff --git a/completions/bash b/completions/bash index dc2cd508..c28cbd25 100644 --- a/completions/bash +++ b/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 diff --git a/completions/fish b/completions/fish index 9d8a679f..1ff1ed5b 100644 --- a/completions/fish +++ b/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 diff --git a/completions/zsh b/completions/zsh index 78379fff..b81cfee0 100644 --- a/completions/zsh +++ b/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 ;; diff --git a/doc/yay.8 b/doc/yay.8 index 94f690b6..c321b16e 100644 --- a/doc/yay.8 +++ b/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 diff --git a/go.mod b/go.mod index ab6030c2..53977add 100644 --- a/go.mod +++ b/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 diff --git a/go.sum b/go.sum index 4009f446..47302c20 100644 --- a/go.sum +++ b/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= diff --git a/pkg/settings/config.go b/pkg/settings/config.go index 3e0a9503..be9c0730 100644 --- a/pkg/settings/config.go +++ b/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 })) diff --git a/pkg/settings/parser/parser.go b/pkg/settings/parser/parser.go index 3562898f..a0dda861 100644 --- a/pkg/settings/parser/parser.go +++ b/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: diff --git a/pkg/settings/runtime.go b/pkg/settings/runtime.go index 08ec4a0f..b591f284 100644 --- a/pkg/settings/runtime.go +++ b/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 } diff --git a/vote.go b/vote.go new file mode 100644 index 00000000..0ba92860 --- /dev/null +++ b/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 +}