Merge branch 'master' into travis-exp

This commit is contained in:
Jguer 2018-06-19 11:04:07 +01:00
commit 70fd80c1f1
No known key found for this signature in database
GPG Key ID: 09754DBECF21746F
35 changed files with 2626 additions and 1841 deletions

6
Gopkg.lock generated
View File

@ -5,19 +5,19 @@
branch = "master"
name = "github.com/jguer/go-alpm"
packages = ["."]
revision = "11d6aadda57c8fb4f93969333cb990677d28d4f9"
revision = "1114f773cdfb05f577438f7a0538eccabc9cf012"
[[projects]]
branch = "master"
name = "github.com/mikkeloscar/aur"
packages = ["."]
revision = "9050804dc7d471393053322aaaa40428fbd32de3"
revision = "837b260b8e90895c45737e2e72313fe5bce6f2c4"
[[projects]]
branch = "master"
name = "github.com/mikkeloscar/gopkgbuild"
packages = ["."]
revision = "32274fc52aa8f5eb28711da734179e9aea27b31f"
revision = "2bb4f1f1db67f81fe50f9c1c4ad9db4f20fd6b22"
[solve-meta]
analyzer-name = "dep"

280
README.md
View File

@ -4,12 +4,11 @@ Yet another Yogurt - An AUR Helper written in Go
#### Packages
[![yay](https://img.shields.io/aur/version/yay.svg?label=yay)](https://aur.archlinux.org/packages/yay/) [![yay-bin](https://img.shields.io/aur/version/yay-bin.svg?label=yay-bin)](https://aur.archlinux.org/packages/yay-bin/) [![yay-git](https://img.shields.io/aur/version/yay-git.svg?label=yay-git)](https://aur.archlinux.org/packages/yay-git/) [![GitHub license](https://img.shields.io/badge/license-AGPL-blue.svg)](https://raw.githubusercontent.com/Jguer/yay/master/LICENSE)
[![yay](https://img.shields.io/aur/version/yay.svg?label=yay)](https://aur.archlinux.org/packages/yay/) [![yay-bin](https://img.shields.io/aur/version/yay-bin.svg?label=yay-bin)](https://aur.archlinux.org/packages/yay-bin/) [![yay-git](https://img.shields.io/aur/version/yay-git.svg?label=yay-git)](https://aur.archlinux.org/packages/yay-git/) [![GitHub license](https://img.shields.io/github/license/jguer/yay.svg)](https://github.com/Jguer/yay/blob/master/LICENSE)
There's a point in everyone's life when you feel the need to write an AUR helper because there are only about 20 of them.
So say hi to 20+1.
Yay was created with a few objectives in mind and based on the design of [yaourt](https://github.com/archlinuxfr/yaourt) and [apacman](https://github.com/oshazard/apacman):
Yay was created with a few objectives in mind and based on the design of [yaourt](https://github.com/archlinuxfr/yaourt), [apacman](https://github.com/oshazard/apacman) and [pacaur](https://github.com/rmarquis/pacaur):
* Have almost no dependencies.
* Provide an interface for pacman.
@ -25,15 +24,83 @@ Yay was created with a few objectives in mind and based on the design of [yaourt
* Search narrowing (`yay linux header` will first search linux and then narrow on header)
* No sourcing of PKGBUILD is done
* The binary has no dependencies that pacman doesn't already have.
* Sources build dependencies
* Removes make dependencies at the end of build process
* Advanced dependency solving
* Remove make dependencies at the end of the build process
#### Frequently Asked Questions
## Installation
If you are migrating from another AUR helper you can simply install Yay from
the AUR like any other package.
The initial installation of Yay can be done by cloning the PKGBUILD and
building with makepkg.
```sh
git clone https://aur.archlinux.org/yay.git
cd yay
makepkg -si
```
## Contributing
Contributors are always welcome!
If you plan to make any large changes or changes that may not be 100% agreed
on, we suggest opening an issue detailing your ideas first.
Otherwise send us a pull request and we will be happy to review it.
### Code Style
All code should be formatted through `go fmt`. This tool will automatically
format code for you. Although it is recommended you write code in this style
and just use this tool to catch mistakes.
### Building
Yay is easy to build with its only build dependency being `go` and the
assumption of `base-devel` being installed.
Run `make` to build Yay. This will generate a binary called `yay` in the same
directory as the Makefile.
Run `make test` to test Yay. This will check the code is formatted correctly,
run the code through `go vet` and run unit tests.
Yay's Makefile automatically sets the `GOPATH` to `$PWD/.go`. This makes it easy to
build using the dependencies in `vendor/`. Running manual go commands such as
`go build` will require that you to either set the `GOPATH` manually or `go get`
The dependencies into your own `GOPATH`.
### Vendored Dependencies
Yay depends on a couple of other projects. These are stored in `vendor/` and
are built into Yay at build time. They do not need to be installed separately.
Currently yay Depends on:
* https://github.com/Jguer/go-alpm
* https://github.com/mikkeloscar/gopkgbuild
* https://github.com/mikkeloscar/aur
## Frequently Asked Questions
* Yay does not display colored output. How do I fix it?
Make sure you have the `Color` option in your `/etc/pacman.conf` [#123](https://github.com/Jguer/yay/issues/123)
#### Example of Custom Operations
* Sometimes diffs are printed to the terminal and other times they are pages
via less. How do I fix this?
Yay uses `git diff` to display diffs, by default git tells less to not page
if the output can fit one terminal length. This can be overridden by
exporting your own flags `export LESS=SRX`.
* Yay is not asking me to edit PKGBUILDS and I don't like diff menu! What do?
`yay --editmenu --nodiffmenu --save`
* Only act on AUR packages or only on repo packages?
`yay -{OPERATION} --aur`
`yay -{OPERATION} --repo`
## Examples of Custom Operations
* `yay <Search Term>` presents package selection menu
* `yay -Ps` prints system statistics
@ -41,197 +108,12 @@ Yay was created with a few objectives in mind and based on the design of [yaourt
* `yay -Yc` cleans unneeded dependencies
* `yay -G` downloads PKGBUILD from ABS or AUR
* `yay -Y --gendb` generates development package DB used for devel updates.
* `yay -Syu --devel --timeupdate` Normal update but also check for development
package updates and uses PKGBUILD modification time and not version to
determine update
<img src="http://jguer.github.io/yay/yayupgrade.png" width="450">
<img src="http://jguer.github.io/yay/yay2.png" width="450">
<img src="http://jguer.github.io/yay/yay4.png" width="450">
## Images
### Changelog
#### v5.675
* Recursively remove dependencies when using yay -Yc
* Highlight diff between old and new versions better
* Fix regression where repo upgrades were marked as deps during sysupgrades
* Added `--editorflags` to add flags to editor execution
#### v5.657
* By default running `yay` will trigger `yay -Syu`
* Updated Shell completions
* `-Ss` shows difference between installed and in-repo versions
* Allow sorting AUR results by fields other than votes
* votes|popularity|id|baseid|name|base|submitted|modified
* Added flags for automatic menu input
* --answerclean --answeredit --answerupgrade
* --noanswerclean --noansweredit --noanswerupgrade
* Fixed versioned dep checking
* Usual fixes to parsing, dependency sourcing and other PKGBUILD atrocities
#### v5.608
* Updated Shell completions
* Added `-Qu` to extended pacman options
* Provides now supported in `-Si`
* Improved build method
* Improved conflict checking
* PKGBUILDs with unsupported arch can force build now
* PGP Key automatic importing
* GPG option passing
* `db/name` support re-added
#### 4.505
* `yay` used to auto save permanent configuration options, now `--save` must be passed to save permanent configuration options
* Competions updated
* Number menu is now used to edit PKGBuilds and Clean Builds
* Devel updates of `-git` packages now uses `git ls-remote` which makes it compatible with other platforms besides github.
* Devel update checking is faster as well
* Updated man page
#### 3.440
* Closed a lot of issues
* Updated bash and zsh completions
* New colour scheme
* Small parsing fixes
* Automatically delete package from transaction if $EDITOR exits with non-zero #140
* Added check depends support
#### 3.373
* Version bump to V3 to reflect all of the changes to syntax
* `yay -Pd` prints default config
* `yay -Pg` prints current config
* Fixes #174
* Fixes #176
* Fixes -G being unable to download split packages
* Fixes #171
* Fixes -Si failing when given a non existing package on https://github.com/Jguer/yay/pull/155
* Fixes other small bugs on 2.350 without adding new features
#### 2.350
* Adds sudo loop (off by default, enable only by editing config file) #147
* Adds replace package support #154 #134
* Minor display improvements #150 for example
* Fixes GenDB
* Fixes Double options passing to pacman
* Noconfirm works more as expected
* Minor fixes and refactoring
* Yay filters out the repository name if it's included.
* Fixes #122
#### 2.298
* Adds #115
#### 2.296
* New argument parsing @Morganamilo (check manpage or --help for new
information)
* yay -Qstats changed to yay -Ps or yay -P --stats
* yay -Cd changed to yay -Yc or yay -Y --clean
* yay -Pu (--upgrades) prints update list
* yay -Pn (--numberupgrades) prints number of updates
* yay -G also possible through -Yg or -Y --getpkgbuild (yay -G will be
discontinued once it's possible to add options to the getpkgbuild operation)
* yay now counts from 1 instead of 0 @Morganamilo
* Support for ranges when selecting packages @samosaara
* Pacaur style ask all questions first and download first @Morganamilo
* Updated vendor dependencies (Fixes pacman.conf parsing errors and PKGBUILD
parsing errors)
* Updated completions
#### 2.219
* Updated manpage
* Updated --help
* Fixed AUR update fails with large number of packages #59
* Check if package is already in upgrade list and skip it. #60
* Add -V and -h for flag parsing @AnthonyLam
* Prevent file corruption by truncating the files @maximbaz
* Print VCS error details @maximbaz
* Using '-' doesn't raise an error @PietroCarrara
* use Command.Dir in aur.PkgInstall; Fixes #32 #47 @afg984
* Suffix YayConf.BuildDir with uid to avoid permission issues @afg984 (Not included in last changelog)
#### 2.200
* Development github package support re-added
#### 2.196
* XDG_CONFIG_HOME support
* XDG_CACHE_HOME support
#### 2.165
* Upgrade list now allows skipping upgrade install
#### 2.159
* Qstats now warns about packages not available in AUR
#### 2.152
* Fetching backend changed to Mikkel Oscar's [Aur](https://github.com/mikkeloscar/aur)
* Added support for development packages from github.
* Pacman backend rewritten and simplified
* Added config framework.
#### 1.115
* Added AUR completions (updates on first completion every 48h)
#### 1.101
* Search speed and quality improved [#3](https://github.com/Jguer/yay/issues/3)
#### 1.100
* Added manpage
* Improved search [#3](https://github.com/Jguer/yay/issues/3)
* Added -G to get pkgbuild from the AUR or ABS. [#6](https://github.com/Jguer/yay/issues/6)
* Fixed [#8](https://github.com/Jguer/yay/issues/8)
* Completed and decluttered zsh completions
* If `$EDITOR` or `$VISUAL` is not set yay will prompt you for an editor [#7](https://github.com/Jguer/yay/issues/7)
#### 1.91
* `--downtop` has been replaced with `--bottomup` (as is logical)
* `yay -Ssq` and `yay -Sqs` now displays AUR packages with less information
* Repository search now uses the same criteria as pacman
#### 1.85
* yay now does -Si for AUR packages
* Fixed package install bugs
#### 1.83
* Added new dependency resolver for future features
* Sort package statistics
#### 1.80
* yay now warns when installing orphan packages
* Added orphan status to number menu
* Qstats now checks if system has orphan packages installed
#### 1.78
* Added foreign package statistics to Qstats
* Group installing is now possible
* Better handling of package dependency installing
#### 1.76
* Fixed critical bug that prevented AUR dependencies from being installed.
#### 1.70
* Stable for everyday use
* Bottom up package display
* Number menu like yaourt/apacman
* System package statistics
<img src="https://cdn.rawgit.com/Jguer/jguer.github.io/5412b8d6/yay/yay-ps.png" width="450">
<img src="https://cdn.rawgit.com/Jguer/jguer.github.io/5412b8d6/yay/yayupgrade.png" width="450">
<img src="https://cdn.rawgit.com/Jguer/jguer.github.io/5412b8d6/yay/yaysearch.png" width="450">

View File

@ -124,7 +124,7 @@ func cleanAUR(keepInstalled, keepCurrent, removeAll bool) error {
// Most people probably don't use keep current and that is the only
// case where this is needed.
// Querying the AUR is slow and needs internet so dont do it if we
// Querying the AUR is slow and needs internet so don't do it if we
// don't need to.
if keepCurrent {
info, err := aurInfo(cachedPackages, &aurWarnings{})
@ -138,7 +138,11 @@ func cleanAUR(keepInstalled, keepCurrent, removeAll bool) error {
}
for _, pkg := range remotePackages {
installedBases.set(pkg.Name())
if pkg.Base() != "" {
installedBases.set(pkg.Base())
} else {
installedBases.set(pkg.Name())
}
}
for _, file := range files {

141
cmd.go
View File

@ -35,6 +35,9 @@ New operations:
yay {-P --print} [options]
yay {-G --getpkgbuild} [package(s)]
New options:
--repo Assume targets are from the repositories
-a --aur Assume targets are from the AUR
Permanent configuration options:
--save Causes the following options to be saved back to the
config file when used
@ -53,13 +56,23 @@ Permanent configuration options:
--config <file> pacman.conf file to use
--requestsplitn <n> Max amount of packages to query per AUR request
--sortby <field> Sort AUR results by a specific field during search
--sortby <field> Sort AUR results by a specific field during search
--answerclean <a> Set a predetermined answer for the clean build menu
--answerdiff <a> Set a predetermined answer for the diff menu
--answeredit <a> Set a predetermined answer for the edit pkgbuild menu
--answerupgrade <a> Set a predetermined answer for the upgrade menu
--noanswerclean Unset the answer for the clean build menu
--noanswerdiff Unset the answer for the edit diff menu
--noansweredit Unset the answer for the edit pkgbuild menu
--noanswerupgrade Unset the answer for the upgrade menu
--cleanmenu Give the option to clean build PKGBUILDS
--diffmenu Give the option to show diffs for build files
--editmenu Give the option to edit/view PKGBUILDS
--upgrademenu Show a detailed list of updates with the option to skip any
--nocleanmenu Don't clean build PKGBUILDS
--nodiffmenu Don't show diffs for build files
--noeditmenu Don't edit/view PKGBUILDS
--noupgrademenu Don't show the upgrade menu
--afterclean Remove package sources after successful install
--noafterclean Do not remove package sources after successful build
@ -77,6 +90,10 @@ Permanent configuration options:
--redownload Always download pkgbuilds of targets
--noredownload Skip pkgbuild download if in cache and up to date
--redownloadall Always download pkgbuilds of all AUR packages
--provides Look for matching provders when searching for packages
--noprovides Just look for packages by pkgname
--pgpfetch Prompt to import PGP keys from PKGBUILDs
--nopgpfetch Don't prompt to import PGP keys
--sudoloop Loop sudo calls in the background to avoid timeout
--nosudoloop Do not loop sudo calls in the background
@ -91,6 +108,7 @@ Print specific options:
-n --numberupgrades Print number of updates
-s --stats Display system package statistics
-u --upgrades Print update list
-w --news Print arch news
Yay specific options:
-c --clean Remove unneeded dependencies
@ -252,6 +270,10 @@ func handleConfig(option, value string) bool {
config.AnswerClean = value
case "noanswerclean":
config.AnswerClean = ""
case "answerdiff":
config.AnswerDiff = value
case "noanswerdiff":
config.AnswerDiff = ""
case "answeredit":
config.AnswerEdit = value
case "noansweredit":
@ -295,6 +317,34 @@ func handleConfig(option, value string) bool {
config.SudoLoop = true
case "nosudoloop":
config.SudoLoop = false
case "provides":
config.Provides = true
case "noprovides":
config.Provides = false
case "pgpfetch":
config.PGPFetch = true
case "nopgpfetch":
config.PGPFetch = false
case "upgrademenu":
config.UpgradeMenu = true
case "noupgrademenu":
config.UpgradeMenu = false
case "cleanmenu":
config.CleanMenu = true
case "nocleanmenu":
config.CleanMenu = false
case "diffmenu":
config.DiffMenu = true
case "nodiffmenu":
config.DiffMenu = false
case "editmenu":
config.EditMenu = true
case "noeditmenu":
config.EditMenu = false
case "a", "aur":
mode = ModeAUR
case "repo":
mode = ModeRepo
default:
return false
}
@ -318,6 +368,8 @@ func handlePrint() (err error) {
err = printNumberOfUpdates()
case cmdArgs.existsArg("u", "upgrades"):
err = printUpdateList(cmdArgs)
case cmdArgs.existsArg("w", "news"):
err = printNewsFeed()
case cmdArgs.existsArg("c", "complete"):
switch {
case cmdArgs.existsArg("f", "fish"):
@ -350,22 +402,21 @@ func handleYay() (err error) {
}
func handleGetpkgbuild() (err error) {
err = getPkgbuilds(cmdArgs.formatTargets())
err = getPkgbuilds(cmdArgs.targets)
return
}
func handleYogurt() (err error) {
options := cmdArgs.formatArgs()
targets := cmdArgs.formatTargets()
config.SearchMode = NumberMenu
err = numberMenu(targets, options)
err = numberMenu(cmdArgs.targets, options)
return
}
func handleSync() (err error) {
targets := cmdArgs.formatTargets()
targets := cmdArgs.targets
if cmdArgs.existsArg("y", "refresh") {
arguments := cmdArgs.copy()
@ -374,7 +425,7 @@ func handleSync() (err error) {
arguments.delArg("s", "search")
arguments.delArg("i", "info")
arguments.delArg("l", "list")
arguments.targets = make(stringSet)
arguments.clearTargets()
err = passToPacman(arguments)
if err != nil {
return
@ -407,30 +458,50 @@ func handleSync() (err error) {
}
func handleRemove() (err error) {
removeVCSPackage(cmdArgs.formatTargets())
removeVCSPackage(cmdArgs.targets)
err = passToPacman(cmdArgs)
return
}
// NumberMenu presents a CLI for selecting packages to install.
func numberMenu(pkgS []string, flags []string) (err error) {
aurQ, aurErr := narrowSearch(pkgS, true)
numaq := len(aurQ)
repoQ, numpq, err := queryRepo(pkgS)
if err != nil {
return
pkgS = removeInvalidTargets(pkgS)
var aurErr error
var repoErr error
var aq aurQuery
var pq repoQuery
var lenaq int
var lenpq int
if mode == ModeAUR || mode == ModeAny {
aq, aurErr = narrowSearch(pkgS, true)
lenaq = len(aq)
}
if mode == ModeRepo || mode == ModeAny {
pq, lenpq, repoErr = queryRepo(pkgS)
if repoErr != nil {
return err
}
}
if numpq == 0 && numaq == 0 {
return fmt.Errorf("no packages match search")
if lenpq == 0 && lenaq == 0 {
return fmt.Errorf("No packages match search")
}
if config.SortMode == BottomUp {
aurQ.printSearch(numpq + 1)
repoQ.printSearch()
if mode == ModeAUR || mode == ModeAny {
aq.printSearch(lenpq + 1)
}
if mode == ModeRepo || mode == ModeAny {
pq.printSearch()
}
} else {
repoQ.printSearch()
aurQ.printSearch(numpq + 1)
if mode == ModeRepo || mode == ModeAny {
pq.printSearch()
}
if mode == ModeAUR || mode == ModeAny {
aq.printSearch(lenpq + 1)
}
}
if aurErr != nil {
@ -457,8 +528,8 @@ func numberMenu(pkgS []string, flags []string) (err error) {
isInclude := len(exclude) == 0 && len(otherExclude) == 0
for i, pkg := range repoQ {
target := len(repoQ) - i
for i, pkg := range pq {
target := len(pq) - i
if config.SortMode == TopDown {
target = i + 1
}
@ -471,10 +542,10 @@ func numberMenu(pkgS []string, flags []string) (err error) {
}
}
for i, pkg := range aurQ {
target := len(aurQ) - i + len(repoQ)
for i, pkg := range aq {
target := len(aq) - i + len(pq)
if config.SortMode == TopDown {
target = i + 1 + len(repoQ)
target = i + 1 + len(pq)
}
if isInclude && include.get(target) {
@ -512,7 +583,7 @@ func passToPacman(args *arguments) error {
argArr = append(argArr, "--")
argArr = append(argArr, args.formatTargets()...)
argArr = append(argArr, args.targets...)
cmd = exec.Command(argArr[0], argArr[1:]...)
@ -544,7 +615,7 @@ func passToPacmanCapture(args *arguments) (string, string, error) {
argArr = append(argArr, "--")
argArr = append(argArr, args.formatTargets()...)
argArr = append(argArr, args.targets...)
cmd = exec.Command(argArr[0], argArr[1:]...)
cmd.Stdout = &outbuf
@ -588,7 +659,6 @@ func passToMakepkgCapture(dir string, args ...string) (string, string, error) {
args = append(args, mflags...)
cmd := exec.Command(config.MakepkgBin, args...)
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
cmd.Dir = dir
cmd.Stdout = &outbuf
cmd.Stderr = &errbuf
@ -615,3 +685,22 @@ func passToGit(dir string, _args ...string) (err error) {
err = cmd.Run()
return
}
func passToGitCapture(dir string, _args ...string) (string, string, error) {
var outbuf, errbuf bytes.Buffer
gitflags := strings.Fields(config.GitFlags)
args := []string{"-C", dir}
args = append(args, gitflags...)
args = append(args, _args...)
cmd := exec.Command(config.GitBin, args...)
cmd.Dir = dir
cmd.Stdout = &outbuf
cmd.Stderr = &errbuf
err := cmd.Run()
stdout := outbuf.String()
stderr := errbuf.String()
return stdout, stderr, err
}

View File

@ -51,6 +51,9 @@ _pacman_pkg() {
)"
}
_yay() {
local common core cur database prev query remove sync upgrade yays print o
COMPREPLY=()
@ -65,14 +68,16 @@ _yay() {
'c g i l p s u w y')
upgrade=('asdeps asexplicit force needed nodeps assume-installed print recursive' 'p')
yays=('clean gendb' 'c')
print=('complete defaultconfig config numberupgrades stats upgrades' 'c d g n
s u')
print=('complete defaultconfig config numberupgrades stats upgrades news' 'c d g n
s u w')
common=('arch cachedir color config confirm dbpath debug gpgdir help hookdir logfile
noconfirm noprogressbar noscriptlet quiet save mflags buildir editor
makepkg pacman tar git gpg gpgflags config requestsplitn sudoloop nosudoloop
redownload noredownload redownloadall rebuild rebuildall rebuildtree norebuild
sortby answerclean answeredit answerupgrade noanswerclean noansweredit
noanswerupgrade root verbose' 'b d h q r v')
sortby answerclean answerdiff answeredit answerupgrade noanswerclean noanswerdiff
noansweredit noanswerupgrade cleanmenu diffmenu editmenu upgrademenu
nocleanmenu nodiffmenu noupgrademenu provides noprovides pgpfetch nopgpfetch
root verbose aur repo' 'a b d h q r v')
core=('database files help query remove sync upgrade version' 'D F Q R S U V h')
for o in 'D database' 'F files' 'Q query' 'R remove' 'S sync' 'U upgrade' 'Y yays' 'P print'; do

View File

@ -3,6 +3,8 @@
# Updated for yay by jguer
set -l progname yay
complete -e -c $progname
complete -c $progname -f
set -l listinstalled "(pacman -Q | string replace ' ' \t)"
# This might be an issue if another package manager is also installed (e.g. for containers)
@ -10,7 +12,7 @@ set -l listall "(yay -Pcf)"
set -l listrepos "(__fish_print_pacman_repos)"
set -l listgroups "(pacman -Sg)\t'Package Group'"
set -l listpacman "(__fish_print_packages)"
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 Y -s G -s V -s S -s D -s Q -s R -s U -s T -s F database query sync remove upgrade deptest files'
set -l database '__fish_contains_opt -s D database'
set -l getpkgbuild '__fish_contains_opt -s G getpkgbuild'
set -l print '__fish_contains_opt -s P print'
@ -21,30 +23,29 @@ set -l upgrade '__fish_contains_opt -s U upgrade'
set -l files '__fish_contains_opt -s F files'
set -l yayspecific '__fish_contains_opt -s Y yay'
complete -c pacman -e
complete -c pacman -f
# HACK: We only need these two to coerce fish to stop file completion and complete options
complete -c $progname -n $noopt -a "-D" -d "Modify the package database"
complete -c $progname -n $noopt -a "-Q" -d "Query the package database"
# complete -c $progname -n $noopt -a "-D" -d "Modify the package database"
# complete -c $progname -n $noopt -a "-Q" -d "Query the package database"
# Primary operations
complete -c $progname -s D -f -l database -n $noopt -d 'Modify the package database'
complete -c $progname -s Q -f -l query -n $noopt -d 'Query the package database'
complete -c $progname -s F -f -l files -n $noopt -d 'Query the files database'
complete -c $progname -s G -f -l getpkgbuild -n $noopt -d 'Get PKGBUILD from ABS or AUR'
complete -c $progname -s P -f -l print -n $noopt -d 'Print information'
complete -c $progname -s Q -f -l query -n $noopt -d 'Query the package database'
complete -c $progname -s R -f -l remove -n $noopt -d 'Remove packages from the system'
complete -c $progname -s S -f -l sync -n $noopt -d 'Synchronize packages'
complete -c $progname -s T -f -l deptest -n $noopt -d 'Check if dependencies are installed'
complete -c $progname -s U -f -l upgrade -n $noopt -d 'Upgrade or add a local package'
complete -c $progname -s F -f -l files -n $noopt -d 'Query the files database'
complete -c $progname -s G -f -l getpkgbuild -n $noopt -d 'Get PKGBUILD from ABS or AUR'
complete -c $progname -s P -f -l print -n $noopt -d 'Print information'
complete -c $progname -s Y -f -l yay -n $noopt -d 'Yay specific operations'
complete -c $progname -s V -f -l version -d 'Display version and exit'
complete -c $progname -s h -f -l help -d 'Display help'
complete -c $progname -s V -f -l version -n $noopt -d 'Display version and exit'
complete -c $progname -s h -f -l help -n $noopt -d 'Display help'
# General options
# Only offer these once a command has been given so they get prominent display
complete -c $progname -n "not $noopt" -s a -l aur -d 'Assume targets are from the repositories'
complete -c $progname -n "not $noopt" -l repo -d 'Assume targets are from the AUR'
complete -c $progname -n "not $noopt" -s b -l dbpath -d 'Alternative database location' -xa '(__fish_complete_directories)'
complete -c $progname -n "not $noopt" -s r -l root -d 'Alternative installation root'
complete -c $progname -n "not $noopt" -s v -l verbose -d 'Output more status messages'
@ -73,6 +74,7 @@ complete -c $progname -n "not $noopt" -l mflags -d 'Pass the following options t
complete -c $progname -n "not $noopt" -l gpgflags -d 'Pass the following options to gpg' -f
complete -c $progname -n "not $noopt" -l buildir -d 'Specify the build directory' -f
complete -c $progname -n "not $noopt" -l editor -d 'Editor to use' -f
complete -c $progname -n "not $noopt" -l editorflags -d 'Editor flags to use' -f
complete -c $progname -n "not $noopt" -l makepkg -d 'Makepkg command to use' -f
complete -c $progname -n "not $noopt" -l pacman -d 'Pacman command to use' -f
complete -c $progname -n "not $noopt" -l tar -d 'Tar command to use' -f
@ -98,16 +100,32 @@ complete -c $progname -n "not $noopt" -l noanswerclean -d 'Unset the answer for
complete -c $progname -n "not $noopt" -l noansweredit -d 'Unset the answer for the edit pkgbuild menu' -f
complete -c $progname -n "not $noopt" -l noanswerupgrade -d 'Unset the answer for the upgrade menu' -f
complete -c $progname -n "not $noopt" -l cleanmenu -d 'Give the option to clean build PKGBUILDS' -f
complete -c $progname -n "not $noopt" -l diffmenu -d 'Give the option to show diffs for build files' -f
complete -c $progname -n "not $noopt" -l editmenu -d 'Give the option to edit/view PKGBUILDS' -f
complete -c $progname -n "not $noopt" -l upgrademenu -d 'Show a detailed list of updates with the option to skip any' -f
complete -c $progname -n "not $noopt" -l nocleanmenu -d 'Do not clean build PKGBUILDS' -f
complete -c $progname -n "not $noopt" -l nodiffmenu -d 'Do not show diffs for build files' -f
complete -c $progname -n "not $noopt" -l noeditmenu -d 'Do not edit/view PKGBUILDS' -f
complete -c $progname -n "not $noopt" -l noupgrademenu -d 'Do not show the upgrade menu' -f
complete -c $progname -n "not $noopt" -l provides -d 'Look for matching provders when searching for packages'
complete -c $progname -n "not $noopt" -l noprovides -d 'Just look for packages by pkgname'
complete -c $progname -n "not $noopt" -l pgpfetch -d 'Prompt to import PGP keys from PKGBUILDs'
complete -c $progname -n "not $noopt" -l nopgpfetch -d 'Do not prompt to import PGP keys'
# Yay options
complete -c $progname -n $yayspecific -s c -l clean -d 'Remove unneeded dependencies' -f
complete -c $progname -n $yayspecific -s g -l getpkgbuild -d 'Download PKGBUILD from ABS or AUR' -xa "$listall" -f
complete -c $progname -n $yayspecific -l gendb -d 'Display system package statistics' -f
complete -c $progname -n $yayspecific -l gendb -d 'Generate development package DB' -f
# Print options
complete -c $progname -n $print -s d -l defaultconfig -d 'Print current yay configuration' -f
complete -c $progname -n $print -s n -l numberupgrades -d 'Print number of updates' -f
complete -c $progname -n $print -s s -l stats -d 'Display system package statistics' -f
complete -c $progname -n $print -s u -l upgrades -d 'Print update list' -f
complete -c $progname -n $print -s w -l news -d 'Print arch news'
complete -c $progname -n $print -s q -l quiet -d 'Do not print news description'
# Transaction options (sync, remove, upgrade)
for condition in sync remove upgrade
@ -195,8 +213,7 @@ complete -c $progname -n "$files" -s q -l quiet -d 'Show less information' -f
complete -c $progname -n "$files" -l machinereadable -d 'Show in machine readable format: repo\0pkgname\0pkgver\0path\n' -f
# Upgrade options
# Theoretically, pacman reads packages in all formats that libarchive supports
# In practice, it's going to be tar.xz or tar.gz
# Using "pkg.tar.*" here would change __fish_complete_suffix's descriptions to "unknown"
complete -c $progname -n "$upgrade" -xa '(__fish_complete_suffix pkg.tar.xz)' -d 'Package file'
complete -c $progname -n "$upgrade" -xa '(__fish_complete_suffix pkg.tar.gz)' -d 'Package file'
complete -c $progname -n "$upgrade" -xa '(__fish_complete_suffix pkg.tar.lzo)' -d 'Package file'
complete -c $progname -n "$upgrade" -xa '(__fish_complete_suffix pkg.tar)' -d 'Package file'

View File

@ -22,6 +22,9 @@ _pacman_opts_commands=(
# options for passing to _arguments: options common to all commands
_pacman_opts_common=(
'--repo[Assume targets are from the repositories]'
{-a,--aur}'[Assume targets are from the AUR]'
'--arch[Set an alternate architecture]'
{-b,--dbpath}'[Alternate database location]:database_location:_files -/'
'--color[colorize the output]:color options:(always never auto)'
@ -43,6 +46,7 @@ _pacman_opts_common=(
'--builddir[Directory to use for building AUR Packages]:build dir:_files -/'
'--editor[Editor to use when editing PKGBUILDs]:editor:_files'
'--editorflags[Flags to pass to editor]'
'--makepkg[makepkg command to use]:makepkg:_files'
'--pacman[pacman command to use]:pacman:_files'
'--tar[bsdtar command to use]:tar:_files'
@ -52,11 +56,18 @@ _pacman_opts_common=(
'--sortby[Sort AUR results by a specific field during search]:sortby options:(votes popularity id baseid name base submitted modified)'
'--answerclean[Set a predetermined answer for the clean build menu]:answer'
'--answeredit[Set a predetermined answer for the edit pkgbuild menu]:answer'
'--answerupgrade[Set a predetermined answer for the upgrade menu]:answe'
'--answerupgrade[Set a predetermined answer for the upgrade menu]:answer'
'--noanswerclean[Unset the answer for the clean build menu]'
'--noansweredit[Unset the answer for the edit pkgbuild menu]'
'--noanswerupgrade[Unset the answer for the upgrade menu]'
'--cleanmenu[Give the option to clean build PKGBUILDS]'
'--diffmenu[Give the option to show diffs for build files]'
'--editmenu[Give the option to edit/view PKGBUILDS]'
'--upgrademenu[Show a detailed list of updates with the option to skip any]'
"--nocleanmenu[Don't clean build PKGBUILDS]"
"--nodiffmenu[Don't show diffs for build files]"
"--noeditmenu[Don't edit/view PKGBUILDS]"
"--noupgrademenu[Don't show the upgrade menu]"
'--bottomup[Show AUR packages first]'
'--topdown[Show repository packages first]'
@ -71,6 +82,10 @@ _pacman_opts_common=(
'--noredownload[Skip pkgbuild download if in cache and up to date]'
'--rebuild[Always build target packages]'
'--rebuildall[Always build all AUR packages]'
'--provides[Look for matching provders when searching for packages]'
'--noprovides[Just look for packages by pkgname]'
'--pgpfetch[Prompt to import PGP keys from PKGBUILDs]'
"--nopgpfetch[Don't prompt to import PGP keys]"
'--rebuildtree[Always build all AUR packages even if installed]'
'--norebuild[Skip package build if in cache and up to date]'
'--mflags[Pass arguments to makepkg]:mflags'
@ -134,6 +149,7 @@ _pacman_opts_print_modifiers=(
{-n,--numberupgrades}'[Print number of updates]'
{-s,--stats}'[Display system package statistics]'
{-u,--upgrades}'[Print update list]'
{-w,--news}'[Print arch news]'
)
# options for passing to _arguments: options for --remove command
_pacman_opts_remove=(

View File

@ -25,6 +25,14 @@ const (
TopDown
)
type targetMode int
const (
ModeAUR targetMode = iota
ModeRepo
ModeAny
)
// Configuration stores yay's config.
type Configuration struct {
BuildDir string `json:"buildDir"`
@ -37,6 +45,7 @@ type Configuration struct {
ReDownload string `json:"redownload"`
ReBuild string `json:"rebuild"`
AnswerClean string `json:"answerclean"`
AnswerDiff string `json:"answerdiff"`
AnswerEdit string `json:"answeredit"`
AnswerUpgrade string `json:"answerupgrade"`
GitBin string `json:"gitbin"`
@ -54,6 +63,12 @@ type Configuration struct {
Devel bool `json:"devel"`
CleanAfter bool `json:"cleanAfter"`
GitClone bool `json:"gitclone"`
Provides bool `json:"provides"`
PGPFetch bool `json:"pgpfetch"`
UpgradeMenu bool `json:"upgrademenu"`
CleanMenu bool `json:"cleanmenu"`
DiffMenu bool `json:"diffmenu"`
EditMenu bool `json:"editmenu"`
}
var version = "5.688"
@ -97,6 +112,9 @@ var alpmConf alpm.PacmanConfig
// AlpmHandle is the alpm handle used by yay.
var alpmHandle *alpm.Handle
// Mode is used to restrict yay to AUR or repo only modes
var mode targetMode = ModeAny
func readAlpmConfig(pacmanconf string) (conf alpm.PacmanConfig, err error) {
file, err := os.Open(pacmanconf)
if err != nil {
@ -134,6 +152,7 @@ func defaultSettings(config *Configuration) {
config.MakepkgBin = "makepkg"
config.NoConfirm = false
config.PacmanBin = "pacman"
config.PGPFetch = true
config.PacmanConf = "/etc/pacman.conf"
config.GpgFlags = ""
config.MFlags = ""
@ -149,9 +168,15 @@ func defaultSettings(config *Configuration) {
config.ReDownload = "no"
config.ReBuild = "no"
config.AnswerClean = ""
config.AnswerDiff = ""
config.AnswerEdit = ""
config.AnswerUpgrade = ""
config.GitClone = true
config.Provides = true
config.UpgradeMenu = true
config.CleanMenu = true
config.DiffMenu = true
config.EditMenu = false
}
// Editor returns the preferred system editor.

View File

@ -1,354 +0,0 @@
package main
import (
"fmt"
"strings"
"sync"
alpm "github.com/jguer/go-alpm"
gopkg "github.com/mikkeloscar/gopkgbuild"
)
// Checks a single conflict against every other to be installed package's
// name and its provides.
func checkInnerConflict(name string, conflict string, conflicts map[string]stringSet, dc *depCatagories) {
deps, err := gopkg.ParseDeps([]string{conflict})
if err != nil {
return
}
dep := deps[0]
for _, pkg := range dc.Aur {
if name == pkg.Name {
continue
}
version, err := gopkg.NewCompleteVersion(pkg.Version)
if err != nil {
return
}
if dep.Name == pkg.Name && version.Satisfies(dep) {
addMapStringSet(conflicts, name, pkg.Name)
continue
}
for _, provide := range pkg.Provides {
// Provides are not versioned unless explicitly defined as
// such. If a conflict is versioned but a provide is
// not it can not conflict.
if (dep.MaxVer != nil || dep.MinVer != nil) && !strings.ContainsAny(provide, "><=") {
continue
}
var version *gopkg.CompleteVersion
var err error
pname, pversion := splitNameFromDep(provide)
if dep.Name != pname {
continue
}
if pversion != "" {
version, err = gopkg.NewCompleteVersion(provide)
if err != nil {
return
}
}
if version != nil && version.Satisfies(dep) {
addMapStringSet(conflicts, name, pkg.Name)
break
}
}
}
for _, pkg := range dc.Repo {
if name == pkg.Name() {
continue
}
version, err := gopkg.NewCompleteVersion(pkg.Version())
if err != nil {
return
}
if dep.Name == pkg.Name() && version.Satisfies(dep) {
addMapStringSet(conflicts, name, pkg.Name())
continue
}
pkg.Provides().ForEach(func(provide alpm.Depend) error {
// Provides are not versioned unless explicitly defined as
// such. If a conflict is versioned but a provide is
// not it can not conflict.
if (dep.MaxVer != nil || dep.MinVer != nil) && provide.Mod == alpm.DepModAny {
return nil
}
if dep.Name != pkg.Name() {
return nil
}
if provide.Mod == alpm.DepModAny {
addMapStringSet(conflicts, name, pkg.Name())
return fmt.Errorf("")
}
version, err := gopkg.NewCompleteVersion(provide.Version)
if err != nil {
return nil
}
if version.Satisfies(dep) {
addMapStringSet(conflicts, name, pkg.Name())
return fmt.Errorf("")
}
return nil
})
}
}
// Checks every to be installed package's conflicts against every other to be
// installed package and its provides.
func checkForInnerConflicts(dc *depCatagories) map[string]stringSet {
conflicts := make(map[string]stringSet)
for _, pkg := range dc.Aur {
for _, cpkg := range pkg.Conflicts {
checkInnerConflict(pkg.Name, cpkg, conflicts, dc)
}
}
for _, pkg := range dc.Repo {
pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
checkInnerConflict(pkg.Name(), conflict.String(), conflicts, dc)
return nil
})
}
return conflicts
}
// Checks a provide or packagename from a to be installed package
// against every already installed package's conflicts
func checkReverseConflict(name string, provide string, conflicts map[string]stringSet) error {
var version *gopkg.CompleteVersion
var err error
localDb, err := alpmHandle.LocalDb()
if err != nil {
return err
}
pname, pversion := splitNameFromDep(provide)
if pversion != "" {
version, err = gopkg.NewCompleteVersion(pversion)
if err != nil {
return nil
}
}
localDb.PkgCache().ForEach(func(pkg alpm.Package) error {
if name == pkg.Name() {
return nil
}
pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
deps, err := gopkg.ParseDeps([]string{conflict.String()})
if err != nil {
return nil
}
dep := deps[0]
// Provides are not versioned unless explicitly defined as
// such. If a conflict is versioned but a provide is
// not it can not conflict.
if (dep.MaxVer != nil || dep.MinVer != nil) && version == nil {
return nil
}
if dep.Name != pname {
return nil
}
if version == nil || version.Satisfies(dep) {
// Todo
addMapStringSet(conflicts, name, pkg.Name()+" ("+provide+")")
return fmt.Errorf("")
}
return nil
})
return nil
})
return nil
}
// Checks the conflict of a to be installed package against the package name and
// provides of every installed package.
func checkConflict(name string, conflict string, conflicts map[string]stringSet) error {
localDb, err := alpmHandle.LocalDb()
if err != nil {
return err
}
deps, err := gopkg.ParseDeps([]string{conflict})
if err != nil {
return nil
}
dep := deps[0]
localDb.PkgCache().ForEach(func(pkg alpm.Package) error {
if name == pkg.Name() {
return nil
}
version, err := gopkg.NewCompleteVersion(pkg.Version())
if err != nil {
return nil
}
if dep.Name == pkg.Name() && version.Satisfies(dep) {
addMapStringSet(conflicts, name, pkg.Name())
return nil
}
pkg.Provides().ForEach(func(provide alpm.Depend) error {
if dep.Name != provide.Name {
return nil
}
// Provides arent version unless explicitly defined as
// such. If a conflict is versioned but a provide is
// not it can not conflict.
if (dep.MaxVer != nil || dep.MinVer != nil) && provide.Mod == alpm.DepModAny {
return nil
}
if provide.Mod == alpm.DepModAny {
addMapStringSet(conflicts, name, pkg.Name()+" ("+provide.Name+")")
return fmt.Errorf("")
}
version, err := gopkg.NewCompleteVersion(provide.Version)
if err != nil {
return nil
}
if version.Satisfies(dep) {
addMapStringSet(conflicts, name, pkg.Name()+" ("+provide.Name+")")
return fmt.Errorf("")
}
return nil
})
return nil
})
return nil
}
// Checks every to be installed package's conflicts against the names and
// provides of every already installed package and checks every to be installed
// package's name and provides against every already installed package.
func checkForConflicts(dc *depCatagories) (map[string]stringSet, error) {
conflicts := make(map[string]stringSet)
for _, pkg := range dc.Aur {
for _, cpkg := range pkg.Conflicts {
checkConflict(pkg.Name, cpkg, conflicts)
}
}
for _, pkg := range dc.Repo {
pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
checkConflict(pkg.Name(), conflict.String(), conflicts)
return nil
})
}
for _, pkg := range dc.Aur {
checkReverseConflict(pkg.Name, pkg.Name, conflicts)
for _, ppkg := range pkg.Provides {
checkReverseConflict(pkg.Name, ppkg, conflicts)
}
}
for _, pkg := range dc.Repo {
checkReverseConflict(pkg.Name(), pkg.Name(), conflicts)
pkg.Provides().ForEach(func(provide alpm.Depend) error {
checkReverseConflict(pkg.Name(), provide.String(), conflicts)
return nil
})
}
return conflicts, nil
}
// Combiles checkForConflicts() and checkForInnerConflicts() in parallel and
// does some printing.
func checkForAllConflicts(dc *depCatagories) error {
var err error
var conflicts map[string]stringSet
var innerConflicts map[string]stringSet
var wg sync.WaitGroup
wg.Add(2)
fmt.Println(bold(cyan("::") + bold(" Checking for conflicts...")))
go func() {
conflicts, err = checkForConflicts(dc)
wg.Done()
}()
fmt.Println(bold(cyan("::") + bold(" Checking for inner conflicts...")))
go func() {
innerConflicts = checkForInnerConflicts(dc)
wg.Done()
}()
wg.Wait()
if err != nil {
return err
}
if len(innerConflicts) != 0 {
fmt.Println()
fmt.Println(bold(red(arrow)), bold("Inner conflicts found:"))
for name, pkgs := range innerConflicts {
str := red(bold(smallArrow)) + " " + name + ":"
for pkg := range pkgs {
str += " " + cyan(pkg)
}
fmt.Println(str)
}
return fmt.Errorf("Unresolvable package conflicts, aborting")
}
if len(conflicts) != 0 {
fmt.Println()
fmt.Println(bold(red(arrow)), bold("Package conflicts found:"))
for name, pkgs := range conflicts {
str := red(bold(smallArrow)) + " Installing " + cyan(name) + " will remove:"
for pkg := range pkgs {
str += " " + cyan(pkg)
}
fmt.Println(str)
}
fmt.Println()
}
return nil
}

170
dep.go Normal file
View File

@ -0,0 +1,170 @@
package main
import (
"fmt"
"strings"
alpm "github.com/jguer/go-alpm"
rpc "github.com/mikkeloscar/aur"
)
type providers struct {
lookfor string
Pkgs []*rpc.Pkg
}
func makeProviders(name string) providers {
return providers{
name,
make([]*rpc.Pkg, 0),
}
}
func (q providers) Len() int {
return len(q.Pkgs)
}
func (q providers) Less(i, j int) bool {
if q.lookfor == q.Pkgs[i].Name {
return true
}
if q.lookfor == q.Pkgs[j].Name {
return false
}
return lessRunes([]rune(q.Pkgs[i].Name), []rune(q.Pkgs[j].Name))
}
func (q providers) Swap(i, j int) {
q.Pkgs[i], q.Pkgs[j] = q.Pkgs[j], q.Pkgs[i]
}
func splitDep(dep string) (string, string, string) {
mod := ""
split := strings.FieldsFunc(dep, func(c rune) bool {
match := c == '>' || c == '<' || c == '='
if match {
mod += string(c)
}
return match
})
if len(split) == 1 {
return split[0], "", ""
}
return split[0], mod, split[1]
}
func pkgSatisfies(name, version, dep string) bool {
depName, depMod, depVersion := splitDep(dep)
if depName != name {
return false
}
return verSatisfies(version, depMod, depVersion)
}
func provideSatisfies(provide, dep string) bool {
depName, depMod, depVersion := splitDep(dep)
provideName, provideMod, provideVersion := splitDep(provide)
if provideName != depName {
return false
}
// Unversioned provieds can not satisfy a versioned dep
if provideMod == "" && depMod != "" {
return false
}
return verSatisfies(provideVersion, depMod, depVersion)
}
func verSatisfies(ver1, mod, ver2 string) bool {
switch mod {
case "=":
return alpm.VerCmp(ver1, ver2) == 0
case "<":
return alpm.VerCmp(ver1, ver2) < 0
case "<=":
return alpm.VerCmp(ver1, ver2) <= 0
case ">":
return alpm.VerCmp(ver1, ver2) > 0
case ">=":
return alpm.VerCmp(ver1, ver2) >= 0
}
return true
}
func satisfiesAur(dep string, pkg *rpc.Pkg) bool {
if pkgSatisfies(pkg.Name, pkg.Version, dep) {
return true
}
for _, provide := range pkg.Provides {
if provideSatisfies(provide, dep) {
return true
}
}
return false
}
func satisfiesRepo(dep string, pkg *alpm.Package) bool {
if pkgSatisfies(pkg.Name(), pkg.Version(), dep) {
return true
}
if pkg.Provides().ForEach(func(provide alpm.Depend) error {
if provideSatisfies(provide.String(), dep) {
return fmt.Errorf("")
}
return nil
}) != nil {
return true
}
return false
}
//split apart db/package to db and package
func splitDbFromName(pkg string) (string, string) {
split := strings.SplitN(pkg, "/", 2)
if len(split) == 2 {
return split[0], split[1]
}
return "", split[0]
}
func getBases(pkgs map[string]*rpc.Pkg) map[string][]*rpc.Pkg {
bases := make(map[string][]*rpc.Pkg)
for _, pkg := range pkgs {
_, ok := bases[pkg.PackageBase]
if !ok {
bases[pkg.PackageBase] = make([]*rpc.Pkg, 0)
}
bases[pkg.PackageBase] = append(bases[pkg.PackageBase], pkg)
}
return bases
}
func isDevelName(name string) bool {
for _, suffix := range []string{"git", "svn", "hg", "bzr", "nightly"} {
if strings.HasSuffix(name, "-"+suffix) {
return true
}
}
return strings.Contains(name, "-always-")
}

275
depCheck.go Normal file
View File

@ -0,0 +1,275 @@
package main
import (
"fmt"
"strings"
"sync"
alpm "github.com/jguer/go-alpm"
// gopkg "github.com/mikkeloscar/gopkgbuild"
)
func (dp *depPool) checkInnerConflict(name string, conflict string, conflicts mapStringSet) {
for _, pkg := range dp.Aur {
if pkg.Name == name {
continue
}
if satisfiesAur(conflict, pkg) {
conflicts.Add(name, pkg.Name)
}
}
for _, pkg := range dp.Repo {
if pkg.Name() == name {
continue
}
if satisfiesRepo(conflict, pkg) {
conflicts.Add(name, pkg.Name())
}
}
}
func (dp *depPool) checkForwardConflict(name string, conflict string, conflicts mapStringSet) {
dp.LocalDb.PkgCache().ForEach(func(pkg alpm.Package) error {
if pkg.Name() == name || dp.hasPackage(pkg.Name()) {
return nil
}
if satisfiesRepo(conflict, &pkg) {
n := pkg.Name()
if n != conflict {
n += " (" + conflict + ")"
}
conflicts.Add(name, n)
}
return nil
})
}
func (dp *depPool) checkReverseConflict(name string, conflict string, conflicts mapStringSet) {
for _, pkg := range dp.Aur {
if pkg.Name == name {
continue
}
if satisfiesAur(conflict, pkg) {
if name != conflict {
name += " (" + conflict + ")"
}
conflicts.Add(pkg.Name, name)
}
}
for _, pkg := range dp.Repo {
if pkg.Name() == name {
continue
}
if satisfiesRepo(conflict, pkg) {
if name != conflict {
name += " (" + conflict + ")"
}
conflicts.Add(pkg.Name(), name)
}
}
}
func (dp *depPool) checkInnerConflicts(conflicts mapStringSet) {
for _, pkg := range dp.Aur {
for _, conflict := range pkg.Conflicts {
dp.checkInnerConflict(pkg.Name, conflict, conflicts)
}
}
for _, pkg := range dp.Repo {
pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
dp.checkInnerConflict(pkg.Name(), conflict.String(), conflicts)
return nil
})
}
}
func (dp *depPool) checkForwardConflicts(conflicts mapStringSet) {
for _, pkg := range dp.Aur {
for _, conflict := range pkg.Conflicts {
dp.checkForwardConflict(pkg.Name, conflict, conflicts)
}
}
for _, pkg := range dp.Repo {
pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
dp.checkForwardConflict(pkg.Name(), conflict.String(), conflicts)
return nil
})
}
}
func (dp *depPool) checkReverseConflicts(conflicts mapStringSet) {
dp.LocalDb.PkgCache().ForEach(func(pkg alpm.Package) error {
if dp.hasPackage(pkg.Name()) {
return nil
}
pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
dp.checkReverseConflict(pkg.Name(), conflict.String(), conflicts)
return nil
})
return nil
})
}
func (dp *depPool) CheckConflicts() error {
var wg sync.WaitGroup
innerConflicts := make(mapStringSet)
conflicts := make(mapStringSet)
wg.Add(2)
fmt.Println(bold(cyan("::") + bold(" Checking for conflicts...")))
go func() {
dp.checkForwardConflicts(conflicts)
dp.checkReverseConflicts(conflicts)
wg.Done()
}()
fmt.Println(bold(cyan("::") + bold(" Checking for inner conflicts...")))
go func() {
dp.checkInnerConflicts(innerConflicts)
wg.Done()
}()
wg.Wait()
if len(innerConflicts) != 0 {
fmt.Println()
fmt.Println(bold(red(arrow)), bold("Inner conflicts found:"))
for name, pkgs := range innerConflicts {
str := red(bold(smallArrow)) + " " + name + ":"
for pkg := range pkgs {
str += " " + cyan(pkg) + ","
}
str = strings.TrimSuffix(str, ",")
fmt.Println(str)
}
return fmt.Errorf("Unresolvable package conflicts, aborting")
}
if len(conflicts) != 0 {
fmt.Println()
fmt.Println(bold(red(arrow)), bold("Package conflicts found:"))
for name, pkgs := range conflicts {
str := red(bold(smallArrow)) + " Installing " + cyan(name) + " will remove:"
for pkg := range pkgs {
str += " " + cyan(pkg) + ","
}
str = strings.TrimSuffix(str, ",")
fmt.Println(str)
}
fmt.Println()
}
return nil
}
type missing struct {
Good stringSet
Missing map[string][][]string
}
func (dp *depPool) _checkMissing(dep string, stack []string, missing *missing) {
if missing.Good.get(dep) {
return
}
if trees, ok := missing.Missing[dep]; ok {
for _, tree := range trees {
if stringSliceEqual(tree, stack) {
return
}
}
missing.Missing[dep] = append(missing.Missing[dep], stack)
return
}
aurPkg := dp.findSatisfierAur(dep)
if aurPkg != nil {
missing.Good.set(dep)
for _, deps := range [3][]string{aurPkg.Depends, aurPkg.MakeDepends, aurPkg.CheckDepends} {
for _, aurDep := range deps {
if _, err := dp.LocalDb.PkgCache().FindSatisfier(aurDep); err == nil {
missing.Good.set(aurDep)
continue
}
dp._checkMissing(aurDep, append(stack, aurPkg.Name), missing)
}
}
return
}
repoPkg := dp.findSatisfierRepo(dep)
if repoPkg != nil {
missing.Good.set(dep)
repoPkg.Depends().ForEach(func(repoDep alpm.Depend) error {
if _, err := dp.LocalDb.PkgCache().FindSatisfier(repoDep.String()); err == nil {
missing.Good.set(repoDep.String())
return nil
}
dp._checkMissing(repoDep.String(), append(stack, repoPkg.Name()), missing)
return nil
})
return
}
missing.Missing[dep] = [][]string{stack}
}
func (dp *depPool) CheckMissing() error {
missing := &missing{
make(stringSet),
make(map[string][][]string),
}
for _, target := range dp.Targets {
dp._checkMissing(target.DepString(), make([]string, 0), missing)
}
if len(missing.Missing) == 0 {
return nil
}
fmt.Println(bold(red(arrow+" Error: ")) + "Could not find all required packages:")
for dep, trees := range missing.Missing {
for _, tree := range trees {
fmt.Print(" ", cyan(dep))
if len(tree) == 0 {
fmt.Print(" (Target")
} else {
fmt.Print(" (Wanted by: ")
for n := 0; n < len(tree)-1; n++ {
fmt.Print(cyan(tree[n]), " -> ")
}
fmt.Print(cyan(tree[len(tree)-1]))
}
fmt.Println(")")
}
}
return fmt.Errorf("")
}

120
depOrder.go Normal file
View File

@ -0,0 +1,120 @@
package main
import (
alpm "github.com/jguer/go-alpm"
rpc "github.com/mikkeloscar/aur"
)
type depOrder struct {
Aur []*rpc.Pkg
Repo []*alpm.Package
Runtime stringSet
Bases map[string][]*rpc.Pkg
}
func makeDepOrder() *depOrder {
return &depOrder{
make([]*rpc.Pkg, 0),
make([]*alpm.Package, 0),
make(stringSet),
make(map[string][]*rpc.Pkg),
}
}
func getDepOrder(dp *depPool) *depOrder {
do := makeDepOrder()
for _, target := range dp.Targets {
dep := target.DepString()
aurPkg := dp.Aur[dep]
if aurPkg != nil && pkgSatisfies(aurPkg.Name, aurPkg.Version, dep) {
do.orderPkgAur(aurPkg, dp, true)
}
aurPkg = dp.findSatisfierAur(dep)
if aurPkg != nil {
do.orderPkgAur(aurPkg, dp, true)
}
repoPkg := dp.findSatisfierRepo(dep)
if repoPkg != nil {
do.orderPkgRepo(repoPkg, dp, true)
}
}
return do
}
func (do *depOrder) orderPkgAur(pkg *rpc.Pkg, dp *depPool, runtime bool) {
if runtime {
do.Runtime.set(pkg.Name)
}
delete(dp.Aur, pkg.Name)
for i, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} {
for _, dep := range deps {
aurPkg := dp.findSatisfierAur(dep)
if aurPkg != nil {
do.orderPkgAur(aurPkg, dp, runtime && i == 0)
}
repoPkg := dp.findSatisfierRepo(dep)
if repoPkg != nil {
do.orderPkgRepo(repoPkg, dp, runtime && i == 0)
}
}
}
if _, ok := do.Bases[pkg.PackageBase]; !ok {
do.Aur = append(do.Aur, pkg)
do.Bases[pkg.PackageBase] = make([]*rpc.Pkg, 0)
}
do.Bases[pkg.PackageBase] = append(do.Bases[pkg.PackageBase], pkg)
}
func (do *depOrder) orderPkgRepo(pkg *alpm.Package, dp *depPool, runtime bool) {
if runtime {
do.Runtime.set(pkg.Name())
}
delete(dp.Repo, pkg.Name())
pkg.Depends().ForEach(func(dep alpm.Depend) (err error) {
repoPkg := dp.findSatisfierRepo(dep.String())
if repoPkg != nil {
do.orderPkgRepo(repoPkg, dp, runtime)
}
return nil
})
do.Repo = append(do.Repo, pkg)
}
func (do *depOrder) HasMake() bool {
lenAur := 0
for _, base := range do.Bases {
lenAur += len(base)
}
return len(do.Runtime) != lenAur+len(do.Repo)
}
func (do *depOrder) getMake() []string {
makeOnly := make([]string, 0, len(do.Aur)+len(do.Repo)-len(do.Runtime))
for _, base := range do.Bases {
for _, pkg := range base {
if !do.Runtime.get(pkg.Name) {
makeOnly = append(makeOnly, pkg.Name)
}
}
}
for _, pkg := range do.Repo {
if !do.Runtime.get(pkg.Name()) {
makeOnly = append(makeOnly, pkg.Name())
}
}
return makeOnly
}

477
depPool.go Normal file
View File

@ -0,0 +1,477 @@
package main
import (
"sort"
"strings"
"sync"
alpm "github.com/jguer/go-alpm"
rpc "github.com/mikkeloscar/aur"
)
type target struct {
Db string
Name string
Mod string
Version string
}
func toTarget(pkg string) target {
db, dep := splitDbFromName(pkg)
name, mod, version := splitDep(dep)
return target{
db,
name,
mod,
version,
}
}
func (t target) DepString() string {
return t.Name + t.Mod + t.Version
}
func (t target) String() string {
if t.Db != "" {
return t.Db + "/" + t.DepString()
}
return t.DepString()
}
type depPool struct {
Targets []target
Explicit stringSet
Repo map[string]*alpm.Package
Aur map[string]*rpc.Pkg
AurCache map[string]*rpc.Pkg
Groups []string
LocalDb *alpm.Db
SyncDb alpm.DbList
Warnings *aurWarnings
}
func makeDepPool() (*depPool, error) {
localDb, err := alpmHandle.LocalDb()
if err != nil {
return nil, err
}
syncDb, err := alpmHandle.SyncDbs()
if err != nil {
return nil, err
}
dp := &depPool{
make([]target, 0),
make(stringSet),
make(map[string]*alpm.Package),
make(map[string]*rpc.Pkg),
make(map[string]*rpc.Pkg),
make([]string, 0),
localDb,
syncDb,
nil,
}
return dp, nil
}
// Includes db/ prefixes and group installs
func (dp *depPool) ResolveTargets(pkgs []string) error {
// RPC requests are slow
// Combine as many AUR package requests as possible into a single RPC
// call
aurTargets := make(stringSet)
pkgs = removeInvalidTargets(pkgs)
for _, pkg := range pkgs {
var err error
target := toTarget(pkg)
// skip targets already satisfied
// even if the user enters db/pkg and aur/pkg the latter will
// still get skipped even if it's from a different database to
// the one specified
// this is how pacman behaves
if dp.hasPackage(target.DepString()) {
continue
}
var foundPkg *alpm.Package
var singleDb *alpm.Db
// aur/ prefix means we only check the aur
if target.Db == "aur" || mode == ModeAUR {
dp.Targets = append(dp.Targets, target)
aurTargets.set(target.DepString())
continue
}
// If there'ss a different priefix only look in that repo
if target.Db != "" {
singleDb, err = alpmHandle.SyncDbByName(target.Db)
if err != nil {
return err
}
foundPkg, err = singleDb.PkgCache().FindSatisfier(target.DepString())
//otherwise find it in any repo
} else {
foundPkg, err = dp.SyncDb.FindSatisfier(target.DepString())
}
if err == nil {
dp.Targets = append(dp.Targets, target)
dp.Explicit.set(foundPkg.Name())
dp.ResolveRepoDependency(foundPkg)
continue
} else {
//check for groups
//currently we don't resolve the packages in a group
//only check if the group exists
//would be better to check the groups from singleDb if
//the user specified a db but there's no easy way to do
//it without making alpm_lists so don't bother for now
//db/group is probably a rare use case
group, err := dp.SyncDb.PkgCachebyGroup(target.Name)
if err == nil {
dp.Groups = append(dp.Groups, target.String())
group.ForEach(func(pkg alpm.Package) error {
dp.Explicit.set(pkg.Name())
return nil
})
continue
}
}
//if there was no db prefix check the aur
if target.Db == "" {
aurTargets.set(target.DepString())
}
dp.Targets = append(dp.Targets, target)
}
if len(aurTargets) > 0 && (mode == ModeAny || mode == ModeAUR) {
return dp.resolveAURPackages(aurTargets, true)
}
return nil
}
// Pseudo provides finder.
// Try to find provides by performing a search of the package name
// This effectively performs -Ss on each package
// then runs -Si on each result to cache the information.
//
// For example if you were to -S yay then yay -Ss would give:
// yay-git yay-bin yay realyog pacui pacui-git ruby-yard
// These packages will all be added to the cache in case they are needed later
// Ofcouse only the first three packages provide yay, the rest are just false
// positives.
//
// This method increases dependency resolve time
func (dp *depPool) findProvides(pkgs stringSet) error {
var mux sync.Mutex
var wg sync.WaitGroup
doSearch := func(pkg string) {
defer wg.Done()
var err error
var results []rpc.Pkg
// Hack for a bigger search result, if the user wants
// java-envronment we can search for just java instead and get
// more hits.
words := strings.Split(pkg, "-")
for i := range words {
results, err = rpc.SearchByNameDesc(strings.Join(words[:i+1], "-"))
if err == nil {
break
}
}
if err != nil {
return
}
for _, result := range results {
mux.Lock()
if _, ok := dp.AurCache[result.Name]; !ok {
pkgs.set(result.Name)
}
mux.Unlock()
}
}
for pkg := range pkgs {
if _, err := dp.LocalDb.PkgByName(pkg); err == nil {
continue
}
wg.Add(1)
go doSearch(pkg)
}
wg.Wait()
return nil
}
func (dp *depPool) cacheAURPackages(_pkgs stringSet) error {
pkgs := _pkgs.copy()
query := make([]string, 0)
for pkg := range pkgs {
if _, ok := dp.AurCache[pkg]; ok {
pkgs.remove(pkg)
}
}
if len(pkgs) == 0 {
return nil
}
if config.Provides {
err := dp.findProvides(pkgs)
if err != nil {
return err
}
}
for pkg := range pkgs {
if _, ok := dp.AurCache[pkg]; !ok {
name, _, _ := splitDep(pkg)
query = append(query, name)
}
}
info, err := aurInfo(query, dp.Warnings)
if err != nil {
return err
}
for _, pkg := range info {
// Dump everything in cache just in case we need it later
dp.AurCache[pkg.Name] = pkg
}
return nil
}
func (dp *depPool) resolveAURPackages(pkgs stringSet, explicit bool) error {
newPackages := make(stringSet)
newAURPackages := make(stringSet)
err := dp.cacheAURPackages(pkgs)
if err != nil {
return err
}
if len(pkgs) == 0 {
return nil
}
for name := range pkgs {
_, ok := dp.Aur[name]
if ok {
continue
}
pkg := dp.findSatisfierAurCache(name)
if pkg == nil {
continue
}
if explicit {
dp.Explicit.set(pkg.Name)
}
dp.Aur[pkg.Name] = pkg
for _, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} {
for _, dep := range deps {
newPackages.set(dep)
}
}
}
for dep := range newPackages {
if dp.hasSatisfier(dep) {
continue
}
//has satisfier installed: skip
_, isInstalled := dp.LocalDb.PkgCache().FindSatisfier(dep)
if isInstalled == nil {
continue
}
//has satisfier in repo: fetch it
repoPkg, inRepos := dp.SyncDb.FindSatisfier(dep)
if inRepos == nil {
dp.ResolveRepoDependency(repoPkg)
continue
}
//assume it's in the aur
//ditch the versioning because the RPC can't handle it
newAURPackages.set(dep)
}
err = dp.resolveAURPackages(newAURPackages, false)
return err
}
func (dp *depPool) ResolveRepoDependency(pkg *alpm.Package) {
dp.Repo[pkg.Name()] = pkg
pkg.Depends().ForEach(func(dep alpm.Depend) (err error) {
//have satisfier in dep tree: skip
if dp.hasSatisfier(dep.String()) {
return
}
//has satisfier installed: skip
_, isInstalled := dp.LocalDb.PkgCache().FindSatisfier(dep.String())
if isInstalled == nil {
return
}
//has satisfier in repo: fetch it
repoPkg, inRepos := dp.SyncDb.FindSatisfier(dep.String())
if inRepos != nil {
return
}
dp.ResolveRepoDependency(repoPkg)
return nil
})
}
func getDepPool(pkgs []string, warnings *aurWarnings) (*depPool, error) {
dp, err := makeDepPool()
if err != nil {
return nil, err
}
dp.Warnings = warnings
err = dp.ResolveTargets(pkgs)
return dp, err
}
func (dp *depPool) findSatisfierAur(dep string) *rpc.Pkg {
for _, pkg := range dp.Aur {
if satisfiesAur(dep, pkg) {
return pkg
}
}
return nil
}
// This is mostly used to promote packages from the cache
// to the Install list
// Provide a pacman style provider menu if there's more than one candidate
// This acts slightly differently from Pacman, It will give
// a menu even if a package with a matching name exists. I believe this
// method is better because most of the time you are choosing between
// foo and foo-git.
// Using Pacman's ways trying to install foo would never give you
// a menu.
// TODO: maybe intermix repo providers in the menu
func (dp *depPool) findSatisfierAurCache(dep string) *rpc.Pkg {
depName, _, _ := splitDep(dep)
seen := make(stringSet)
providers := makeProviders(depName)
if _, err := dp.LocalDb.PkgByName(depName); err == nil {
if pkg, ok := dp.AurCache[dep]; ok && pkgSatisfies(pkg.Name, pkg.Version, dep) {
return pkg
}
}
if cmdArgs.op == "Y" || cmdArgs.op == "yay" {
for _, pkg := range dp.AurCache {
if pkgSatisfies(pkg.Name, pkg.Version, dep) {
for _, target := range dp.Targets {
if target.Name == pkg.Name {
return pkg
}
}
}
}
}
for _, pkg := range dp.AurCache {
if seen.get(pkg.Name) {
continue
}
if pkgSatisfies(pkg.Name, pkg.Version, dep) {
providers.Pkgs = append(providers.Pkgs, pkg)
seen.set(pkg.Name)
continue
}
for _, provide := range pkg.Provides {
if provideSatisfies(provide, dep) {
providers.Pkgs = append(providers.Pkgs, pkg)
seen.set(pkg.Name)
continue
}
}
}
if providers.Len() == 1 {
return providers.Pkgs[0]
}
if providers.Len() > 1 {
sort.Sort(providers)
return providerMenu(dep, providers)
}
return nil
}
func (dp *depPool) findSatisfierRepo(dep string) *alpm.Package {
for _, pkg := range dp.Repo {
if satisfiesRepo(dep, pkg) {
return pkg
}
}
return nil
}
func (dp *depPool) hasSatisfier(dep string) bool {
return dp.findSatisfierRepo(dep) != nil || dp.findSatisfierAur(dep) != nil
}
func (dp *depPool) hasPackage(name string) bool {
for _, pkg := range dp.Repo {
if pkg.Name() == name {
return true
}
}
for _, pkg := range dp.Aur {
if pkg.Name == name {
return true
}
}
for _, pkg := range dp.Groups {
if pkg == name {
return true
}
}
return false
}

View File

@ -1,650 +0,0 @@
package main
import (
"fmt"
"strings"
alpm "github.com/jguer/go-alpm"
rpc "github.com/mikkeloscar/aur"
gopkg "github.com/mikkeloscar/gopkgbuild"
)
type depTree struct {
ToProcess stringSet
Repo map[string]*alpm.Package
Aur map[string]*rpc.Pkg
Missing stringSet
Groups stringSet
Provides map[string]string
Warnings *aurWarnings
}
type depCatagories struct {
Repo []*alpm.Package
Aur []*rpc.Pkg
MakeOnly stringSet
Bases map[string][]*rpc.Pkg
}
func makeDepTree() *depTree {
dt := depTree{
make(stringSet),
make(map[string]*alpm.Package),
make(map[string]*rpc.Pkg),
make(stringSet),
make(stringSet),
make(map[string]string),
&aurWarnings{},
}
return &dt
}
func makeDependCatagories() *depCatagories {
dc := depCatagories{
make([]*alpm.Package, 0),
make([]*rpc.Pkg, 0),
make(stringSet),
make(map[string][]*rpc.Pkg),
}
return &dc
}
// Cut the version requirement from a dependency leaving just the name.
func splitNameFromDep(dep string) (string, string) {
split := strings.FieldsFunc(dep, func(c rune) bool {
return c == '>' || c == '<' || c == '='
})
if len(split) == 1 {
return split[0], ""
}
return split[0], split[1]
}
//split apart db/package to db and package
func splitDbFromName(pkg string) (string, string) {
split := strings.SplitN(pkg, "/", 2)
if len(split) == 2 {
return split[0], split[1]
}
return "", split[0]
}
func isDevelName(name string) bool {
for _, suffix := range []string{"git", "svn", "hg", "bzr", "nightly"} {
if strings.HasSuffix(name, suffix) {
return true
}
}
return strings.Contains(name, "-always-")
}
func getBases(pkgs map[string]*rpc.Pkg) map[string][]*rpc.Pkg {
bases := make(map[string][]*rpc.Pkg)
nextpkg:
for _, pkg := range pkgs {
for _, base := range bases[pkg.PackageBase] {
if base == pkg {
continue nextpkg
}
}
_, ok := bases[pkg.PackageBase]
if !ok {
bases[pkg.PackageBase] = make([]*rpc.Pkg, 0)
}
bases[pkg.PackageBase] = append(bases[pkg.PackageBase], pkg)
}
return bases
}
func aurFindProvider(name string, dt *depTree) (string, *rpc.Pkg) {
dep, _ := splitNameFromDep(name)
aurpkg, exists := dt.Aur[dep]
if exists {
return dep, aurpkg
}
dep, exists = dt.Provides[dep]
if exists {
aurpkg, exists = dt.Aur[dep]
if exists {
return dep, aurpkg
}
}
return "", nil
}
func repoFindProvider(name string, dt *depTree) (string, *alpm.Package) {
dep, _ := splitNameFromDep(name)
alpmpkg, exists := dt.Repo[dep]
if exists {
return dep, alpmpkg
}
dep, exists = dt.Provides[dep]
if exists {
alpmpkg, exists = dt.Repo[dep]
if exists {
return dep, alpmpkg
}
}
return "", nil
}
// Step two of dependency resolving. We already have all the information on the
// packages we need, now it's just about ordering them correctly.
// pkgs is a list of targets, the packages we want to install. Dependencies are
// not included.
// For each package we want we iterate down the tree until we hit the bottom.
// This is done recursively for each branch.
// The start of the tree is defined as the package we want.
// When we hit the bottom of the branch we know thats the first package
// we need to install so we add it to the start of the to install
// list (dc.Aur and dc.Repo).
// We work our way up until there is another branch to go down and do it all
// again.
//
// Here is a visual example:
//
// a
// / \
// b c
// / \
// d e
//
// We see a and it needs b and c
// We see b and it needs d and e
// We see d - it needs nothing so we add d to our list and move up
// We see e - it needs nothing so we add e to our list and move up
// We see c - it needs nothing so we add c to our list and move up
//
// The final install order would come out as debca
//
// There is a little more to this, handling provides, multiple packages wanting the
// same dependencies, etc. This is just the basic premise.
func getDepCatagories(pkgs []string, dt *depTree) (*depCatagories, error) {
dc := makeDependCatagories()
seen := make(stringSet)
dc.Bases = getBases(dt.Aur)
for _, pkg := range pkgs {
dep, alpmpkg := repoFindProvider(pkg, dt)
if alpmpkg != nil {
repoDepCatagoriesRecursive(alpmpkg, dc, dt, false)
dc.Repo = append(dc.Repo, alpmpkg)
delete(dt.Repo, dep)
}
dep, aurpkg := aurFindProvider(pkg, dt)
if aurpkg != nil {
depCatagoriesRecursive(aurpkg, dc, dt, false, seen)
if !seen.get(aurpkg.PackageBase) {
dc.Aur = append(dc.Aur, aurpkg)
seen.set(aurpkg.PackageBase)
}
delete(dt.Aur, dep)
}
}
for _, base := range dc.Bases {
for _, pkg := range base {
for _, dep := range pkg.Depends {
dc.MakeOnly.remove(dep)
}
}
}
for _, pkg := range dc.Repo {
pkg.Depends().ForEach(func(_dep alpm.Depend) error {
dep := _dep.Name
dc.MakeOnly.remove(dep)
return nil
})
}
for _, pkg := range pkgs {
dc.MakeOnly.remove(pkg)
}
dupes := make(map[*alpm.Package]struct{})
filteredRepo := make([]*alpm.Package, 0)
for _, pkg := range dc.Repo {
_, ok := dupes[pkg]
if ok {
continue
}
dupes[pkg] = struct{}{}
filteredRepo = append(filteredRepo, pkg)
}
dc.Repo = filteredRepo
return dc, nil
}
func repoDepCatagoriesRecursive(pkg *alpm.Package, dc *depCatagories, dt *depTree, isMake bool) {
pkg.Depends().ForEach(func(_dep alpm.Depend) error {
dep, alpmpkg := repoFindProvider(_dep.Name, dt)
if alpmpkg != nil {
delete(dt.Repo, dep)
repoDepCatagoriesRecursive(alpmpkg, dc, dt, isMake)
if isMake {
dc.MakeOnly.set(alpmpkg.Name())
}
dc.Repo = append(dc.Repo, alpmpkg)
}
return nil
})
}
func depCatagoriesRecursive(_pkg *rpc.Pkg, dc *depCatagories, dt *depTree, isMake bool, seen stringSet) {
for _, pkg := range dc.Bases[_pkg.PackageBase] {
for _, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} {
for _, pkg := range deps {
dep, aurpkg := aurFindProvider(pkg, dt)
if aurpkg != nil {
delete(dt.Aur, dep)
depCatagoriesRecursive(aurpkg, dc, dt, isMake, seen)
if !seen.get(aurpkg.PackageBase) {
dc.Aur = append(dc.Aur, aurpkg)
seen.set(aurpkg.PackageBase)
}
if isMake {
dc.MakeOnly.set(aurpkg.Name)
}
}
dep, alpmpkg := repoFindProvider(pkg, dt)
if alpmpkg != nil {
delete(dt.Repo, dep)
repoDepCatagoriesRecursive(alpmpkg, dc, dt, isMake)
if isMake {
dc.MakeOnly.set(alpmpkg.Name())
}
dc.Repo = append(dc.Repo, alpmpkg)
}
}
isMake = true
}
}
}
// This is step one for dependency resolving. pkgs is a slice of the packages you
// want to resolve the dependencies for. They can be a mix of aur and repo
// dependencies. All unmet dependencies will be resolved.
//
// For Aur dependencies depends, makedepends and checkdepends are resolved but
// for repo packages only depends are resolved as they are prebuilt.
// The return will be split into three categories: Repo, Aur and Missing.
// The return is in no way ordered. This step is is just aimed at gathering the
// packages we need.
//
// This has been designed to make the least amount of rpc requests as possible.
// Web requests are probably going to be the bottleneck here so minimizing them
// provides a nice speed boost.
//
// Here is a visual expample of the request system.
// Remember only unsatisfied packages are requested, if a package is already
// installed we dont bother.
//
// a
// / \
// b c
// / \
// d e
//
// We see a so we send a request for a
// We see a wants b and c so we send a request for b and c
// We see d and e so we send a request for d and e
//
// Thats 5 packages in 3 requests. The amount of requests needed should always be
// the same as the height of the tree.
// The example does not really do this justice, In the real world where packages
// have 10+ dependencies each this is a very nice optimization.
func getDepTree(pkgs []string, warnings *aurWarnings) (*depTree, error) {
dt := makeDepTree()
dt.Warnings = warnings
localDb, err := alpmHandle.LocalDb()
if err != nil {
return dt, err
}
syncDb, err := alpmHandle.SyncDbs()
if err != nil {
return dt, err
}
for _, pkg := range pkgs {
db, name := splitDbFromName(pkg)
var foundPkg *alpm.Package
var singleDb *alpm.Db
if db == "aur" {
dt.ToProcess.set(name)
continue
}
// Check the repos for a matching dep
if db != "" {
singleDb, err = alpmHandle.SyncDbByName(db)
if err != nil {
return dt, err
}
foundPkg, err = singleDb.PkgCache().FindSatisfier(name)
} else {
foundPkg, err = syncDb.FindSatisfier(name)
}
if err == nil {
repoTreeRecursive(foundPkg, dt, localDb, syncDb)
continue
} else {
//would be better to check the groups from singleDb if
//the user specified a db but theres no easy way to do
//it without making alpm_lists so dont bother for now
//db/group is probably a rare use case
_, err := syncDb.PkgCachebyGroup(name)
if err == nil {
dt.Groups.set(pkg)
continue
}
}
if db == "" {
dt.ToProcess.set(name)
} else {
dt.Missing.set(pkg)
}
}
if len(dt.ToProcess) > 0 {
fmt.Println(bold(cyan("::") + bold(" Querying AUR...")))
}
err = depTreeRecursive(dt, localDb, syncDb, false)
if err != nil {
return dt, err
}
if !cmdArgs.existsArg("d", "nodeps") {
err = checkVersions(dt)
}
dt.Warnings.Print()
return dt, err
}
// Takes a repo package,
// gives all of the non installed deps,
// repeats on each sub dep.
func repoTreeRecursive(pkg *alpm.Package, dt *depTree, localDb *alpm.Db, syncDb alpm.DbList) (err error) {
_, exists := dt.Repo[pkg.Name()]
if exists {
return
}
_, exists = dt.Provides[pkg.Name()]
if exists {
return
}
dt.Repo[pkg.Name()] = pkg
(*pkg).Provides().ForEach(func(dep alpm.Depend) (err error) {
dt.Provides[dep.Name] = pkg.Name()
return nil
})
(*pkg).Depends().ForEach(func(dep alpm.Depend) (err error) {
_, exists := dt.Repo[dep.Name]
if exists {
return
}
_, isInstalled := localDb.PkgCache().FindSatisfier(dep.String())
if isInstalled == nil {
return
}
repoPkg, inRepos := syncDb.FindSatisfier(dep.String())
if inRepos == nil {
repoTreeRecursive(repoPkg, dt, localDb, syncDb)
return
}
dt.Missing.set(dep.String())
return
})
return
}
func depTreeRecursive(dt *depTree, localDb *alpm.Db, syncDb alpm.DbList, isMake bool) (err error) {
if len(dt.ToProcess) == 0 {
return
}
nextProcess := make(stringSet)
currentProcess := make(stringSet)
// Strip version conditions
for _dep := range dt.ToProcess {
dep, _ := splitNameFromDep(_dep)
currentProcess.set(dep)
}
// Assume toprocess only contains aur stuff we have not seen
info, err := aurInfo(currentProcess.toSlice(), dt.Warnings)
if err != nil {
return
}
// Cache the results
for _, pkg := range info {
dt.Aur[pkg.Name] = pkg
for _, provide := range pkg.Provides {
name, _ := splitNameFromDep(provide)
dt.Provides[name] = pkg.Name
}
}
// Loop through to process and check if we now have
// each packaged cached.
// If not cached, we assume it is missing.
for pkgName := range currentProcess {
pkg, exists := dt.Aur[pkgName]
// Did not get it in the request.
if !exists {
dt.Missing.set(pkgName)
continue
}
// for each dep and makedep
for _, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} {
for _, versionedDep := range deps {
dep, _ := splitNameFromDep(versionedDep)
_, exists = dt.Aur[dep]
// We have it cached so skip.
if exists {
continue
}
_, exists = dt.Provides[dep]
// We have it cached so skip.
if exists {
continue
}
_, exists = dt.Repo[dep]
// We have it cached so skip.
if exists {
continue
}
_, exists = dt.Missing[dep]
// We know it does not resolve so skip.
if exists {
continue
}
// Check if already installed.
_, isInstalled := localDb.PkgCache().FindSatisfier(versionedDep)
if isInstalled == nil && config.ReBuild != "tree" {
continue
}
// Check the repos for a matching dep.
repoPkg, inRepos := syncDb.FindSatisfier(versionedDep)
if inRepos == nil {
if isInstalled == nil && config.ReBuild == "tree" {
continue
}
repoTreeRecursive(repoPkg, dt, localDb, syncDb)
continue
}
// If all else fails add it to next search.
nextProcess.set(versionedDep)
}
}
}
dt.ToProcess = nextProcess
depTreeRecursive(dt, localDb, syncDb, true)
return
}
func checkVersions(dt *depTree) error {
has := make(map[string][]string)
allDeps := make([]*gopkg.Dependency, 0)
localDb, err := alpmHandle.LocalDb()
if err != nil {
return err
}
for _, pkg := range dt.Aur {
for _, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} {
for _, dep := range deps {
_, _dep := splitNameFromDep(dep)
if _dep != "" {
deps, _ := gopkg.ParseDeps([]string{dep})
if deps[0] != nil {
allDeps = append(allDeps, deps[0])
}
}
}
}
addMapStringSlice(has, pkg.Name, pkg.Version)
if !isDevelName(pkg.Name) {
for _, name := range pkg.Provides {
_name, _ver := splitNameFromDep(name)
if _ver != "" {
addMapStringSlice(has, _name, _ver)
} else {
delete(has, _name)
}
}
}
}
for _, pkg := range dt.Repo {
pkg.Depends().ForEach(func(dep alpm.Depend) error {
if dep.Mod != alpm.DepModAny {
deps, _ := gopkg.ParseDeps([]string{dep.String()})
if deps[0] != nil {
allDeps = append(allDeps, deps[0])
}
}
return nil
})
addMapStringSlice(has, pkg.Name(), pkg.Version())
pkg.Provides().ForEach(func(dep alpm.Depend) error {
if dep.Mod != alpm.DepModAny {
addMapStringSlice(has, dep.Name, dep.Version)
} else {
delete(has, dep.Name)
}
return nil
})
}
localDb.PkgCache().ForEach(func(pkg alpm.Package) error {
pkg.Provides().ForEach(func(dep alpm.Depend) error {
if dep.Mod != alpm.DepModAny {
addMapStringSlice(has, dep.Name, dep.Version)
} else {
delete(has, dep.Name)
}
return nil
})
return nil
})
for _, dep := range allDeps {
satisfied := false
verStrs, ok := has[dep.Name]
if !ok {
continue
}
for _, verStr := range verStrs {
version, err := gopkg.NewCompleteVersion(verStr)
if err != nil {
return err
}
if version.Satisfies(dep) {
satisfied = true
break
}
}
if !satisfied {
dt.Missing.set(dep.String())
}
}
return nil
}

178
doc/yay.8
View File

@ -1,5 +1,5 @@
'\" t
.TH "YAY" "8" "2018-02-29" "Yay v3\&.460+" "Yay Manual"
.TH "YAY" "8" "2018\-06\-04" "Yay v6\&.784+" "Yay Manual"
.nh
.ad l
.SH "NAME"
@ -20,29 +20,29 @@ This manpage only covers options unique to Yay\&. For other options see
\fBpacman(8)\fR\&.
.SH "YAY OPERATIONS"
.PP
\fB\-Y, --yay\fR
\fB\-Y, \-\-yay\fR
.RS 4
Perform yay specific operations\&. This is the default if no other operation is
selected\&.
.RE
.PP
\fB\-P, --print\fR
\fB\-P, \-\-print\fR
.RS 4
Perform yay specific print operations\&.
.RE
.PP
\fB\-G, --getpkgbuild\fR
\fB\-G, \-\-getpkgbuild\fR
.RS 4
Downloads PKGBUILD from ABS or AUR\&.
.RE
.PP
If no arguments are provided 'yay -Syu' will be performed\&.
If no arguments are provided 'yay \-Syu' will be performed\&.
.RE
.PP
If no operation is selected -Y will be assumed\&.
If no operation is selected \-Y will be assumed\&.
.SH "EXTENDED PACMAN OPERATIONS"
.PP
\fB\-S, -Si, -Ss, -Su, -Sc, -Qu\fR
\fB\-S, \-Si, \-Ss, \-Su, \-Sc, \-Qu\fR
.RS 4
These operations are extended to support both AUR and repo packages\&.
.RE
@ -58,9 +58,23 @@ sources or built packages but will keep already downloaded vcs sources\&.
\fB\-R\fR
.RS 4
Yay will also remove cached data about devel packages\&.
.RE
.SH "YAY OPTIONS (APPLY TO -Y AND --YAY)"
.SH "NEW OPTIONS"
.PP
\fB \-\-repo\fR
.RS 4
Assume all targets are from the repositories\&. Additionally Actions such as
sysupgrade will only act on repository packages\&.
.RE
\fB\-a \-\-aur\fR
.RS 4
Assume all targets are from the AUR\&. Additionally Actions such as
sysupgrade will only act on AUR packages\&.
Note that dependency resolving will still act as normal and include repository
packages\&.
.RE
.SH "YAY OPTIONS (APPLY TO \-Y AND \-\-YAY)"
.PP
\fB<NO OPTION>\fR
.RS 4
@ -80,7 +94,7 @@ used when migrating to Yay from another AUR helper.
.RS 4
Remove unneeded dependencies\&.
.RE
.SH "PRINT OPTIONS (APPLY TO -P AND --PRINT)"
.SH "PRINT OPTIONS (APPLY TO \-P AND \-\-PRINT)"
\fB\-c \-\-complete\fR
.RS 4
Print a list of all AUR and repo packages\&. This is to allow shell completion
@ -105,20 +119,33 @@ Print current yay configuration\&.
\fB\-n \-\-numberupgrades\fR
.RS 4
Print number of packages that need to be updated\&. Note this does not perform
a database refresh\&. Run \fByay -Sy\fR Before this for an up to date result\&.
a database refresh\&. Run \fByay \-Sy\fR Before this for an up to date result\&.
.RE
.PP
\fB\-s \-\-stats\fR
.RS 4
Displays information about installed packages and system health\&. If there are
orphaned, out-of-date or packages that no longer exist on the AUR warnings will
orphaned, out\-of\-date or packages that no longer exist on the AUR warnings will
be displayed\&.
.RE
.PP
\fB\-u \-\-upgrades\fR
.RS 4
Print Names of packages that need to be updated\&. Note this does not perform
a database refresh\&. Run \fByay -Sy\fR Before this for an up to date result\&.
a database refresh\&. Run \fByay \-Sy\fR Before this for an up to date result\&.
.RE
.PP
\fB\-w \-\-news\fR
.RS 4
Print new news from the Archlinux homepage\&. News is considered new if it is
newer than the build date of all native packages\&. Pass this twice to show all
available news\&.
.RE
.PP
\fB\-q \-\-quiet\fR
.RS 4
Only show titles when printing news\&.
.RE
.PP
.SH "PERMANENT CONFIGURATION SETTINGS"
.PP
@ -205,6 +232,13 @@ will be used instead of reading from standard input but will be treated exactly
the same when parsed\&.
.RE
.PP
\fB\-\-answerdiff <All|None|Installed|NotInstalled|...>\fR
.RS 4
Set a predetermined answer for the edit diff menu question\&. This answer
will be used instead of reading from standard input but will be treated exactly
the same when parsed\&.
.RE
.PP
\fB\-\-answeredit <All|None|Installed|NotInstalled|...>\fR
.RS 4
Set a predetermined answer for the edit pkgbuild menu question\&. This answer
@ -224,6 +258,11 @@ reading from standard input but will be treated exactly the same\&.
Unset the answer for the clean build menu\&.
.RE
.PP
\fB\-\-noanswerdiff\fR
.RS 4
Unset the answer for the diff menu\&.
.RE
.PP
\fB\-\-noansweredit\fR
.RS 4
Unset the answer for the edit pkgbuild menu\&.
@ -234,6 +273,59 @@ Unset the answer for the edit pkgbuild menu\&.
Unset the answer for the upgrade menu\&.
.RE
.PP
\fB\-\-cleanmenu\fR
.RS 4
Show the clean menu\&. This menu gives you the chance to fully delete the
downloaded build files from Yay's cache before redownloing a fresh copy\&.
.RE
.PP
\fB\-\-diffmenu\fR
.RS 4
Show the diff menu\&. This menu gives you the option to view diffs from
build files before building\&.
.RE
.PP
\fB\-\-editmenu\fR
.RS 4
Show the edit menu\&. This menu gives you the option to edit or view PKGBUILDs
before building\&.
\fBWarning\fR: Yay resolves dependencies ahead of time via the RPC\&. It is not
recommended to edit pkgbuild variables unless you know what you are doing\&.
.RE
.PP
\fB\-\-upgrademenu\fR
.RS 4
Show a detailed list of updates in a similar format to VerbosePkgLists\&.
Upgrades can also be skipped using numbers, number ranges or repo names\&.
Adidionally ^ can be used to invert the selection\&.
\fBWarning\fR: It is not recommended to skip updates from the repositores as
this can lead to partial upgrades\&. This feature is intended to easily skip AUR
updates on the fly that may be broken or have a long compile time\&. Ultimately
it is up to the user what upgrades they skip\&.
.RE
.PP
\fB\-\-nocleanmenu\fR
.RS 4
Do not show the clean menu\&.
.RE
.PP
\fB\-\-nodiffmenu\fR
.RS 4
Do not show the diff menu\&.
.RE
.PP
\fB\-\-noeditmenu\fR
.RS 4
Do not show the edit menu\&.
.RE
.PP
\fB\-\-noupgrademenu\fR
.RS 4
Do not show the upgrade menu\&.
.RE
.PP
\fB\-\-topdown\fR
.RS 4
Display repository packages first and then AUR packages\&.
@ -258,9 +350,9 @@ Do not check for development packages updates during sysupgrade\&.
\fB\-\-gitclone\fR
.RS 4
Use git to download and update PKGBUILDs\&. PKGBUILDs previously downloaded
using tarball will continue to use tarballs until the package is clean built\&.
Similarly, PKGBUILDs managed with git will continue to use git until the package
is clean built.\&.
using tarball will continue to use tarballs until the package is clean
built\&. Similarly, PKGBUILDs managed with git will continue to use git until
the package is clean built.\&.
.RE
.PP
\fB\-\-nogitclone\fR
@ -269,6 +361,18 @@ Download and update PKGBUILDs using tarballs\&. The above conditions about
previously installed packages still apply\&.
.RE
.PP
\fB\-\-showdiffs\fR
.RS 4
Show diffs for build files\&. Diffs are shown via \fBgit diff\fR which uses
less by default\&. This behaviour can be changed via git's config, the
\fB$GIT_PAGER\fR or \fB$PAGER\fR environment variables\&.
.RE
.PP
\fB\-\-noshowdiffs\fR
.RS 4
Show diffs for build files\&. Files will be opened by the editor\%.
.RE
.PP
\fB\-\-afterclean\fR
.RS 4
Remove package sources after successful Install\&.
@ -307,6 +411,32 @@ When downloading pkgbuilds if the pkgbuild is found in cache and is equal or
newer than the AUR's version use that instead of downloading a new one\&.
.RE
.PP
\fB\-\-provides\fR
.RS 4
Look for matching providers when searching for AUR packages\&. When multiple
providers are found a menu will appear prompting you to pick one\&. This
increases dependency resolve time although this should not be noticeable\&.
.RE
.PP
\fB\-\-noprovides\fR
.RS 4
Do not look for matching providers when searching for AUR packages\&. If
multiple providers happen to be found the menu will still appear\&.
.RE
.PP
\fB\-\-pgpfetch\fR
.RS 4
Prompt to import unknown PGP keys from the \fBvalidpgpkeys\fR field of each
PKGBUILD.
.RE
.PP
\fB\-\-pgpfetch\fR
.RS 4
Do not prompt to import unknown PGP keys\&. This is likley to cause a build
failiure unless using options such as \fB\-\-skippgpcheck\fR or a customized
gpg config\%.
.RE
.PP
\fB\-\-rebuild\fR
.RS 4
Always build target packages even when a copy is available in cache\&.
@ -365,37 +495,37 @@ yay \fIfoo\fR
Search and install from the repos and the \fBAUR\fR\ using yogurt mode\&.
.RE
.PP
yay -Syu
yay \-Syu
.RS 4
Update package list and upgrade all currently installed repo and \fBAUR\fR\&.
.RE
.PP
yay -S \fIfoo\fR
yay \-S \fIfoo\fR
.RS 4
Installs package \fIfoo\fR from the repos or the \fBAUR\fR\&.
.RE
.PP
yay -Ss \fIfoo\fR
yay \-Ss \fIfoo\fR
.RS 4
Searches for package \fIfoo\fR on the repos or the \fBAUR\fR\&.
.RE
.PP
yay -Si \fIfoo\fR
yay \-Si \fIfoo\fR
.RS 4
Gets information about package \fIfoo\fR from the repos or the \fBAUR\fR\&.
.RE
.PP
yay -S \fIfoo\fR --mflags "--skipchecksums --skippgpcheck"
yay \-S \fIfoo\fR \-\-mflags "\-\-skipchecksums \-\-skippgpcheck"
.RS 4
Installs \fIfoo\fR while skipping checksums and pgp checks\&.
.RE
.PP
yay --devel --save
yay \-\-devel \-\-save
.RS 4
Sets devel to true in the config\&.
.RE
.PP
yay -P --stats
yay \-P \-\-stats
.RS 4
Shows statistics for installed packages and system health\&.
.RE
@ -435,7 +565,7 @@ and built packages from those packages\&.
\fBPACMAN.CONF\fR
.RS 4
Yay uses Pacman's config file to set certain pacman options either through
go-alpm or Yay itself. Options inherited include most libalpm options and
go\-alpm or Yay itself. Options inherited include most libalpm options and
pacman options\&.
.PP
Notably \fBDatabases\fR, \fBColor\fR and \fB*Path/*Dir\fR options are used\&.

View File

@ -21,11 +21,7 @@ func shouldUseGit(path string) bool {
}
_, err = os.Stat(filepath.Join(path, ".git"))
if os.IsNotExist(err) {
return false
}
return true
return err == nil || os.IsExist(err)
}
func downloadFile(path string, url string) (err error) {
@ -48,25 +44,47 @@ func downloadFile(path string, url string) (err error) {
return err
}
func gitDownload(url string, path string, name string) error {
func gitHasDiff(path string, name string) (bool, error) {
stdout, stderr, err := passToGitCapture(filepath.Join(path, name), "rev-parse", "HEAD")
if err != nil {
return false, fmt.Errorf("%s%s", stderr, err)
}
head := strings.TrimSpace(stdout)
stdout, stderr, err = passToGitCapture(filepath.Join(path, name), "rev-parse", "HEAD@{upstream}")
if err != nil {
return false, fmt.Errorf("%s%s", stderr, err)
}
upstream := strings.TrimSpace(stdout)
return head != upstream, nil
}
func gitDownload(url string, path string, name string) (bool, error) {
_, err := os.Stat(filepath.Join(path, name, ".git"))
if os.IsNotExist(err) {
err = passToGit(path, "clone", url, name)
if err != nil {
return fmt.Errorf("error cloning %s", name)
return false, fmt.Errorf("error cloning %s", name)
}
return nil
return true, nil
} else if err != nil {
return fmt.Errorf("error reading %s", filepath.Join(path, name, ".git"))
return false, fmt.Errorf("error reading %s", filepath.Join(path, name, ".git"))
}
err = passToGit(filepath.Join(path, name), "fetch")
if err != nil {
return fmt.Errorf("error fetching %s", name)
return false, fmt.Errorf("error fetching %s", name)
}
err = passToGit(filepath.Join(path, name), "reset", "--hard", "HEAD")
return false, nil
}
func gitMerge(url string, path string, name string) error {
err := passToGit(filepath.Join(path, name), "reset", "--hard", "HEAD")
if err != nil {
return fmt.Errorf("error resetting %s", name)
}
@ -79,15 +97,20 @@ func gitDownload(url string, path string, name string) error {
return nil
}
func gitDiff(path string, name string) error {
err := passToGit(filepath.Join(path, name), "diff", "HEAD..HEAD@{upstream}")
return err
}
// DownloadAndUnpack downloads url tgz and extracts to path.
func downloadAndUnpack(url string, path string, trim bool) (err error) {
func downloadAndUnpack(url string, path string) (err error) {
err = os.MkdirAll(path, 0755)
if err != nil {
return
}
tokens := strings.Split(url, "/")
fileName := tokens[len(tokens)-1]
fileName := filepath.Base(url)
tarLocation := filepath.Join(path, fileName)
defer os.Remove(tarLocation)
@ -97,13 +120,7 @@ func downloadAndUnpack(url string, path string, trim bool) (err error) {
return
}
if trim {
err = exec.Command("/bin/sh", "-c",
config.TarBin+" --strip-components 2 --include='*/"+fileName[:len(fileName)-7]+"/trunk/' -xf "+tarLocation+" -C "+path).Run()
os.Rename(path+"trunk", path+fileName[:len(fileName)-7]) // kurwa
} else {
err = exec.Command(config.TarBin, "-xf", tarLocation, "-C", path).Run()
}
err = exec.Command(config.TarBin, "-xf", tarLocation, "-C", path).Run()
if err != nil {
return
}
@ -112,23 +129,40 @@ func downloadAndUnpack(url string, path string, trim bool) (err error) {
}
func getPkgbuilds(pkgs []string) error {
//possibleAurs := make([]string, 0, 0)
missing := false
wd, err := os.Getwd()
if err != nil {
return err
}
missing, err := getPkgbuildsfromABS(pkgs, wd)
if err != nil {
return err
pkgs = removeInvalidTargets(pkgs)
aur, repo, err := packageSlices(pkgs)
if len(repo) > 0 {
missing, err = getPkgbuildsfromABS(repo, wd)
if err != nil {
return err
}
}
if len(aur) > 0 {
_missing, err := getPkgbuildsfromAUR(aur, wd)
if err != nil {
return err
}
missing = missing || _missing
}
if missing {
err = fmt.Errorf("")
}
err = getPkgbuildsfromAUR(missing, wd)
return err
}
// GetPkgbuild downloads pkgbuild from the ABS.
func getPkgbuildsfromABS(pkgs []string, path string) (missing []string, err error) {
func getPkgbuildsfromABS(pkgs []string, path string) (missing bool, err error) {
dbList, err := alpmHandle.SyncDbs()
if err != nil {
return
@ -136,8 +170,14 @@ func getPkgbuildsfromABS(pkgs []string, path string) (missing []string, err erro
nextPkg:
for _, pkgN := range pkgs {
pkgDb, name := splitDbFromName(pkgN)
for _, db := range dbList.Slice() {
pkg, err := db.PkgByName(pkgN)
if pkgDb != "" && db.Name() != pkgDb {
continue
}
pkg, err := db.PkgByName(name)
if err == nil {
var url string
name := pkg.Base()
@ -145,52 +185,84 @@ nextPkg:
name = pkg.Name()
}
if db.Name() == "core" || db.Name() == "extra" {
url = "https://projects.archlinux.org/svntogit/packages.git/snapshot/packages/" + name + ".tar.gz"
} else if db.Name() == "community" || db.Name() == "multilib" {
url = "https://projects.archlinux.org/svntogit/community.git/snapshot/community-packages/" + name + ".tar.gz"
} else {
fmt.Println(pkgN + " not in standard repositories")
if _, err := os.Stat(filepath.Join(path, name)); err == nil {
fmt.Println(bold(red(arrow)), bold(cyan(name)), "directory already exists")
continue nextPkg
}
errD := downloadAndUnpack(url, path, true)
if errD != nil {
fmt.Println(bold(red(arrow))+" "+bold(cyan(pkg.Name())), bold(red(errD.Error())))
switch db.Name() {
case "core", "extra":
url = "https://git.archlinux.org/svntogit/packages.git/snapshot/packages/" + name + ".tar.gz"
case "community", "multilib":
url = "https://git.archlinux.org/svntogit/community.git/snapshot/packages/" + name + ".tar.gz"
default:
fmt.Println(pkgN, "not in standard repositories")
continue nextPkg
}
errD := downloadAndUnpack(url, cacheHome)
if errD != nil {
fmt.Println(bold(red(arrow)), bold(cyan(pkg.Name())), bold(red(errD.Error())))
}
errD = exec.Command("mv", filepath.Join(cacheHome, "packages", name, "trunk"), filepath.Join(path, name)).Run()
if errD != nil {
fmt.Println(bold(red(arrow)), bold(cyan(pkg.Name())), bold(red(errD.Error())))
} else {
fmt.Println(bold(yellow(arrow)), "Downloaded", cyan(pkg.Name()), "from ABS")
}
fmt.Println(bold(yellow(arrow)), "Downloaded", cyan(pkg.Name()), "from ABS")
continue nextPkg
}
}
missing = append(missing, pkgN)
fmt.Println(pkgN, "could not find package in database")
missing = true
}
if _, err := os.Stat(filepath.Join(cacheHome, "packages")); err == nil {
os.RemoveAll(filepath.Join(cacheHome, "packages"))
}
return
}
// GetPkgbuild downloads pkgbuild from the AUR.
func getPkgbuildsfromAUR(pkgs []string, dir string) (err error) {
aq, err := aurInfoPrint(pkgs)
func getPkgbuildsfromAUR(pkgs []string, dir string) (bool, error) {
missing := false
strippedPkgs := make([]string, 0)
for _, pkg := range pkgs {
_, name := splitDbFromName(pkg)
strippedPkgs = append(strippedPkgs, name)
}
aq, err := aurInfoPrint(strippedPkgs)
if err != nil {
return err
return missing, err
}
for _, pkg := range aq {
var err error
if _, err := os.Stat(filepath.Join(dir, pkg.PackageBase)); err == nil {
fmt.Println(bold(red(arrow)), bold(cyan(pkg.Name)), "directory already exists")
continue
}
if shouldUseGit(filepath.Join(dir, pkg.PackageBase)) {
err = gitDownload(baseURL+"/"+pkg.PackageBase+".git", dir, pkg.PackageBase)
_, err = gitDownload(baseURL+"/"+pkg.PackageBase+".git", dir, pkg.PackageBase)
} else {
err = downloadAndUnpack(baseURL+aq[0].URLPath, dir, false)
err = downloadAndUnpack(baseURL+aq[0].URLPath, dir)
}
if err != nil {
fmt.Println(err)
} else {
fmt.Println(bold(green(arrow)), bold(green("Downloaded")), bold(magenta(pkg.Name)), bold(green("from AUR")))
fmt.Println(bold(yellow(arrow)), "Downloaded", cyan(pkg.PackageBase), "from AUR")
}
}
return
if len(aq) != len(pkgs) {
missing = true
}
return missing, err
}

View File

@ -15,16 +15,14 @@ import (
// Install handles package installs
func install(parser *arguments) error {
requestTargets := parser.targets.toSlice()
var err error
var incompatible stringSet
var dc *depCatagories
var toClean []*rpc.Pkg
var toEdit []*rpc.Pkg
var do *depOrder
var aurUp upSlice
var repoUp upSlice
requestTargets := parser.copy().targets
warnings := &aurWarnings{}
removeMake := false
@ -41,6 +39,18 @@ func install(parser *arguments) error {
remoteNamesCache := sliceToStringSet(remoteNames)
localNamesCache := sliceToStringSet(localNames)
//create the arguments to pass for the repo install
arguments := parser.copy()
arguments.delArg("y", "refresh")
arguments.delArg("asdeps", "asdep")
arguments.delArg("asexplicit", "asexp")
arguments.op = "S"
arguments.clearTargets()
if mode == ModeAUR {
arguments.delArg("u", "sysupgrade")
}
//if we are doing -u also request all packages needing update
if parser.existsArg("u", "sysupgrade") {
aurUp, repoUp, err = upList(warnings)
@ -48,61 +58,13 @@ func install(parser *arguments) error {
return err
}
for _, up := range aurUp {
requestTargets = append(requestTargets, "aur/"+up.Name)
}
warnings.print()
for _, up := range repoUp {
requestTargets = append(requestTargets, up.Name)
}
}
//if len(aurTargets) > 0 || parser.existsArg("u", "sysupgrade") && len(remoteNames) > 0 {
// fmt.Println(bold(cyan("::") + " Querying AUR..."))
//}
dt, err := getDepTree(requestTargets, warnings)
if err != nil {
return err
}
// Deptree will handle db/pkg prefixes. Now they can be striped from the
// targets.
for pkg := range parser.targets {
_, name := splitDbFromName(pkg)
parser.targets.remove(pkg)
parser.targets.set(name)
}
for i, pkg := range requestTargets {
_, name := splitDbFromName(pkg)
requestTargets[i] = name
}
if len(dt.Missing) > 0 {
str := bold(red(arrow+" Error: ")) + "Could not find all required packages:"
for name := range dt.Missing {
str += "\n " + name
}
return fmt.Errorf("%s", str)
}
//create the arguments to pass for the repo install
arguments := parser.copy()
arguments.delArg("y", "refresh")
arguments.op = "S"
arguments.targets = make(stringSet)
if parser.existsArg("u", "sysupgrade") {
ignore, aurUp, err := upgradePkgs(aurUp, repoUp)
if err != nil {
return err
}
requestTargets = parser.targets.toSlice()
for _, up := range repoUp {
if !ignore.get(up.Name) {
requestTargets = append(requestTargets, up.Name)
@ -114,7 +76,16 @@ func install(parser *arguments) error {
requestTargets = append(requestTargets, up)
}
arguments.addParam("ignore", strings.Join(ignore.toSlice(), ","))
value, _, exists := cmdArgs.getArg("ignore")
if len(ignore) > 0 {
ignoreStr := strings.Join(ignore.toSlice(), ",")
if exists {
ignoreStr += "," + value
}
arguments.options["ignore"] = ignoreStr
}
fmt.Println()
for pkg := range aurUp {
@ -122,91 +93,148 @@ func install(parser *arguments) error {
}
}
hasAur := false
for pkg := range parser.targets {
_, ok := dt.Aur[pkg]
if ok {
hasAur = true
}
}
targets := sliceToStringSet(parser.targets)
if hasAur && 0 == os.Geteuid() {
return fmt.Errorf(bold(red(arrow)) + " Refusing to install AUR Packages as root, Aborting.")
}
dc, err = getDepCatagories(requestTargets, dt)
dp, err := getDepPool(requestTargets, warnings)
if err != nil {
return err
}
for _, pkg := range dc.Repo {
err = dp.CheckMissing()
if err != nil {
return err
}
if len(dp.Aur) == 0 {
parser.op = "S"
parser.delArg("y", "refresh")
parser.options["ignore"] = arguments.options["ignore"]
return passToPacman(parser)
}
if len(dp.Aur) > 0 && 0 == os.Geteuid() {
return fmt.Errorf(bold(red(arrow)) + " Refusing to install AUR Packages as root, Aborting.")
}
err = dp.CheckConflicts()
if err != nil {
return err
}
do = getDepOrder(dp)
if err != nil {
return err
}
for _, pkg := range do.Repo {
arguments.addTarget(pkg.DB().Name() + "/" + pkg.Name())
}
for pkg := range dt.Groups {
for _, pkg := range dp.Groups {
arguments.addTarget(pkg)
}
if len(dc.Aur) == 0 && len(arguments.targets) == 0 && !parser.existsArg("u", "sysupgrade") {
if len(do.Aur) == 0 && len(arguments.targets) == 0 && (!parser.existsArg("u", "sysupgrade") || mode == ModeAUR) {
fmt.Println("There is nothing to do")
return nil
}
if hasAur {
hasAur = len(dc.Aur) != 0
do.Print()
fmt.Println()
err = checkForAllConflicts(dc)
if err != nil {
return err
if do.HasMake() {
if !continueTask("Remove make dependencies after install?", "yY") {
removeMake = true
}
}
printDepCatagories(dc)
fmt.Println()
if len(dc.MakeOnly) > 0 {
if !continueTask("Remove make dependencies after install?", "yY") {
removeMake = true
}
}
toClean, toEdit, err = cleanEditNumberMenu(dc.Aur, dc.Bases, remoteNamesCache)
if config.CleanMenu {
askClean := pkgbuildNumberMenu(do.Aur, do.Bases, remoteNamesCache)
toClean, err := cleanNumberMenu(do.Aur, do.Bases, remoteNamesCache, askClean)
if err != nil {
return err
}
cleanBuilds(toClean)
}
err = downloadPkgBuilds(dc.Aur, parser.targets, dc.Bases)
toSkip := pkgBuildsToSkip(do.Aur, targets)
cloned, err := downloadPkgBuilds(do.Aur, do.Bases, toSkip)
if err != nil {
return err
}
var toDiff []*rpc.Pkg
var toEdit []*rpc.Pkg
if config.DiffMenu {
pkgbuildNumberMenu(do.Aur, do.Bases, remoteNamesCache)
toDiff, err = diffNumberMenu(do.Aur, do.Bases, remoteNamesCache)
if err != nil {
return err
}
if len(toDiff) > 0 {
err = showPkgBuildDiffs(toDiff, do.Bases, cloned)
if err != nil {
return err
}
}
}
if len(toDiff) > 0 {
oldValue := config.NoConfirm
config.NoConfirm = false
fmt.Println()
if !continueTask(bold(green("Proceed with install?")), "nN") {
return fmt.Errorf("Aborting due to user")
}
config.NoConfirm = oldValue
}
err = mergePkgBuilds(do.Aur)
if err != nil {
return err
}
if config.EditMenu {
pkgbuildNumberMenu(do.Aur, do.Bases, remoteNamesCache)
toEdit, err = editNumberMenu(do.Aur, do.Bases, remoteNamesCache)
if err != nil {
return err
}
if len(toEdit) > 0 {
err = editPkgBuilds(toEdit)
err = editPkgBuilds(toEdit, do.Bases)
if err != nil {
return err
}
oldValue := config.NoConfirm
config.NoConfirm = false
if !continueTask(bold(green("Proceed with install?")), "nN") {
return fmt.Errorf("Aborting due to user")
}
config.NoConfirm = oldValue
}
}
//initial srcinfo parse before pkgver() bump
err = parseSRCINFOFiles(dc.Aur, srcinfosStale, dc.Bases)
if err != nil {
return err
if len(toEdit) > 0 {
oldValue := config.NoConfirm
config.NoConfirm = false
fmt.Println()
if !continueTask(bold(green("Proceed with install?")), "nN") {
return fmt.Errorf("Aborting due to user")
}
config.NoConfirm = oldValue
}
incompatible, err = getIncompatible(dc.Aur, srcinfosStale, dc.Bases)
if err != nil {
return err
}
//initial srcinfo parse before pkgver() bump
err = parseSRCINFOFiles(do.Aur, srcinfosStale, do.Bases)
if err != nil {
return err
}
err = checkPgpKeys(dc.Aur, dc.Bases, srcinfosStale)
incompatible, err = getIncompatible(do.Aur, srcinfosStale, do.Bases)
if err != nil {
return err
}
if config.PGPFetch {
err = checkPgpKeys(do.Aur, do.Bases, srcinfosStale)
if err != nil {
return err
}
@ -220,10 +248,19 @@ func install(parser *arguments) error {
depArguments := makeArguments()
depArguments.addArg("D", "asdeps")
expArguments := makeArguments()
expArguments.addArg("D", "asexplicit")
for _, pkg := range dc.Repo {
if !parser.targets.get(pkg.Name()) && !localNamesCache.get(pkg.Name()) && !remoteNamesCache.get(pkg.Name()) {
for _, pkg := range do.Repo {
if !dp.Explicit.get(pkg.Name()) && !localNamesCache.get(pkg.Name()) && !remoteNamesCache.get(pkg.Name()) {
depArguments.addTarget(pkg.Name())
continue
}
if parser.existsArg("asdeps", "asdep") && dp.Explicit.get(pkg.Name()) {
depArguments.addTarget(pkg.Name())
} else if parser.existsArg("asexp", "asexplicit") && dp.Explicit.get(pkg.Name()) {
expArguments.addTarget(pkg.Name())
}
}
@ -233,51 +270,50 @@ func install(parser *arguments) error {
return fmt.Errorf("%s%s", stderr, err)
}
}
if len(expArguments.targets) > 0 {
_, stderr, err := passToPacmanCapture(expArguments)
if err != nil {
return fmt.Errorf("%s%s", stderr, err)
}
}
}
if hasAur {
//conflicts have been checked so answer y for them
ask, _ := strconv.Atoi(cmdArgs.globals["ask"])
uask := alpm.QuestionType(ask) | alpm.QuestionTypeConflictPkg
cmdArgs.globals["ask"] = fmt.Sprint(uask)
//conflicts have been checked so answer y for them
ask, _ := strconv.Atoi(cmdArgs.globals["ask"])
uask := alpm.QuestionType(ask) | alpm.QuestionTypeConflictPkg
cmdArgs.globals["ask"] = fmt.Sprint(uask)
err = downloadPkgBuildsSources(do.Aur, do.Bases, incompatible)
if err != nil {
return err
}
err = buildInstallPkgBuilds(dp, do, srcinfosStale, parser, incompatible)
if err != nil {
return err
}
if removeMake {
removeArguments := makeArguments()
removeArguments.addArg("R", "u")
for _, pkg := range do.getMake() {
removeArguments.addTarget(pkg)
}
oldValue := config.NoConfirm
config.NoConfirm = true
err = passToPacman(removeArguments)
config.NoConfirm = oldValue
err = downloadPkgBuildsSources(dc.Aur, dc.Bases, incompatible)
if err != nil {
return err
}
}
err = buildInstallPkgBuilds(dc.Aur, srcinfosStale, parser.targets, parser, dc.Bases, incompatible)
if err != nil {
return err
}
if len(dc.MakeOnly) > 0 {
if !removeMake {
return nil
}
removeArguments := makeArguments()
removeArguments.addArg("R", "u")
for pkg := range dc.MakeOnly {
removeArguments.addTarget(pkg)
}
oldValue := config.NoConfirm
config.NoConfirm = true
err = passToPacman(removeArguments)
config.NoConfirm = oldValue
if err != nil {
return err
}
}
if config.CleanAfter {
clean(dc.Aur)
}
return nil
if config.CleanAfter {
clean(do.Aur)
}
return nil
@ -318,31 +354,44 @@ nextpkg:
return incompatible, nil
}
func getVersionFromPkgbuild(dir string) (string, error) {
func parsePackageList(dir string) (map[string]string, string, error) {
stdout, stderr, err := passToMakepkgCapture(dir, "--packagelist")
if err != nil {
return "", fmt.Errorf("%s%s", stderr, err)
return nil, "", fmt.Errorf("%s%s", stderr, err)
}
line := strings.Split(stdout, "\n")[0]
split := strings.Split(line, "-")
var version string
lines := strings.Split(stdout, "\n")
pkgdests := make(map[string]string)
if len(split) < 4 {
return "", fmt.Errorf("Can not parse version from: %s", split)
for _, line := range lines {
if line == "" {
continue
}
fileName := filepath.Base(line)
split := strings.Split(fileName, "-")
if len(split) < 4 {
return nil, "", fmt.Errorf("Can not find package name : %s", split)
}
// pkgname-pkgver-pkgrel-arch.pkgext
// This assumes 3 dashes after the pkgname, Will cause an error
// if the PKGEXT contains a dash. Please no one do that.
pkgname := strings.Join(split[:len(split)-3], "-")
version = strings.Join(split[len(split)-3:len(split)-2], "-")
pkgdests[pkgname] = line
}
//pkg-name-pkgver-pkgrel-arch: extract pkgver-pkgrel
ver := split[len(split)-3] + "-" + split[len(split)-2]
return ver, nil
return pkgdests, version, nil
}
func cleanEditNumberMenu(pkgs []*rpc.Pkg, bases map[string][]*rpc.Pkg, installed stringSet) ([]*rpc.Pkg, []*rpc.Pkg, error) {
func pkgbuildNumberMenu(pkgs []*rpc.Pkg, bases map[string][]*rpc.Pkg, installed stringSet) bool {
toPrint := ""
askClean := false
toClean := make([]*rpc.Pkg, 0)
toEdit := make([]*rpc.Pkg, 0)
for n, pkg := range pkgs {
dir := filepath.Join(config.BuildDir, pkg.PackageBase)
@ -362,76 +411,108 @@ func cleanEditNumberMenu(pkgs []*rpc.Pkg, bases map[string][]*rpc.Pkg, installed
fmt.Print(toPrint)
if askClean {
fmt.Println(bold(green(arrow + " Packages to cleanBuild?")))
fmt.Println(bold(green(arrow) + cyan(" [N]one ") + "[A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"))
fmt.Print(bold(green(arrow + " ")))
cleanInput, err := getInput(config.AnswerClean)
if err != nil {
return nil, nil, err
}
return askClean
}
cInclude, cExclude, cOtherInclude, cOtherExclude := parseNumberMenu(cleanInput)
cIsInclude := len(cExclude) == 0 && len(cOtherExclude) == 0
func cleanNumberMenu(pkgs []*rpc.Pkg, bases map[string][]*rpc.Pkg, installed stringSet, hasClean bool) ([]*rpc.Pkg, error) {
toClean := make([]*rpc.Pkg, 0)
if cOtherInclude.get("abort") || cOtherInclude.get("ab") {
return nil, nil, fmt.Errorf("Aborting due to user")
}
if !hasClean {
return toClean, nil
}
if !cOtherInclude.get("n") && !cOtherInclude.get("none") {
for i, pkg := range pkgs {
dir := filepath.Join(config.BuildDir, pkg.PackageBase)
if _, err := os.Stat(dir); os.IsNotExist(err) {
continue
}
fmt.Println(bold(green(arrow + " Packages to cleanBuild?")))
fmt.Println(bold(green(arrow) + cyan(" [N]one ") + "[A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"))
fmt.Print(bold(green(arrow + " ")))
cleanInput, err := getInput(config.AnswerClean)
if err != nil {
return nil, err
}
if !cIsInclude && cExclude.get(len(pkgs)-i) {
continue
}
cInclude, cExclude, cOtherInclude, cOtherExclude := parseNumberMenu(cleanInput)
cIsInclude := len(cExclude) == 0 && len(cOtherExclude) == 0
if installed.get(pkg.Name) && (cOtherInclude.get("i") || cOtherInclude.get("installed")) {
toClean = append(toClean, pkg)
continue
}
if cOtherInclude.get("abort") || cOtherInclude.get("ab") {
return nil, fmt.Errorf("Aborting due to user")
}
if !installed.get(pkg.Name) && (cOtherInclude.get("no") || cOtherInclude.get("notinstalled")) {
toClean = append(toClean, pkg)
continue
}
if !cOtherInclude.get("n") && !cOtherInclude.get("none") {
for i, pkg := range pkgs {
dir := filepath.Join(config.BuildDir, pkg.PackageBase)
if _, err := os.Stat(dir); os.IsNotExist(err) {
continue
}
if cOtherInclude.get("a") || cOtherInclude.get("all") {
toClean = append(toClean, pkg)
continue
}
if !cIsInclude && cExclude.get(len(pkgs)-i) {
continue
}
if cIsInclude && (cInclude.get(len(pkgs)-i) || cOtherInclude.get(pkg.PackageBase)) {
toClean = append(toClean, pkg)
continue
}
if installed.get(pkg.Name) && (cOtherInclude.get("i") || cOtherInclude.get("installed")) {
toClean = append(toClean, pkg)
continue
}
if !cIsInclude && (!cExclude.get(len(pkgs)-i) && !cOtherExclude.get(pkg.PackageBase)) {
toClean = append(toClean, pkg)
continue
}
if !installed.get(pkg.Name) && (cOtherInclude.get("no") || cOtherInclude.get("notinstalled")) {
toClean = append(toClean, pkg)
continue
}
if cOtherInclude.get("a") || cOtherInclude.get("all") {
toClean = append(toClean, pkg)
continue
}
if cIsInclude && (cInclude.get(len(pkgs)-i) || cOtherInclude.get(pkg.PackageBase)) {
toClean = append(toClean, pkg)
continue
}
if !cIsInclude && (!cExclude.get(len(pkgs)-i) && !cOtherExclude.get(pkg.PackageBase)) {
toClean = append(toClean, pkg)
continue
}
}
}
fmt.Println(bold(green(arrow + " PKGBUILDs to edit?")))
return toClean, nil
}
func editNumberMenu(pkgs []*rpc.Pkg, bases map[string][]*rpc.Pkg, installed stringSet) ([]*rpc.Pkg, error) {
return editDiffNumberMenu(pkgs, bases, installed, false)
}
func diffNumberMenu(pkgs []*rpc.Pkg, bases map[string][]*rpc.Pkg, installed stringSet) ([]*rpc.Pkg, error) {
return editDiffNumberMenu(pkgs, bases, installed, true)
}
func editDiffNumberMenu(pkgs []*rpc.Pkg, bases map[string][]*rpc.Pkg, installed stringSet, diff bool) ([]*rpc.Pkg, error) {
toEdit := make([]*rpc.Pkg, 0)
var editInput string
var err error
fmt.Println(bold(green(arrow) + cyan(" [N]one ") + "[A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"))
fmt.Print(bold(green(arrow + " ")))
editInput, err := getInput(config.AnswerEdit)
if err != nil {
return nil, nil, err
if diff {
fmt.Println(bold(green(arrow + " Diffs to show?")))
fmt.Print(bold(green(arrow + " ")))
editInput, err = getInput(config.AnswerDiff)
if err != nil {
return nil, err
}
} else {
fmt.Println(bold(green(arrow + " PKGBUILDs to edit?")))
fmt.Print(bold(green(arrow + " ")))
editInput, err = getInput(config.AnswerEdit)
if err != nil {
return nil, err
}
}
eInclude, eExclude, eOtherInclude, eOtherExclude := parseNumberMenu(editInput)
eIsInclude := len(eExclude) == 0 && len(eOtherExclude) == 0
if eOtherInclude.get("abort") || eOtherInclude.get("ab") {
return nil, nil, fmt.Errorf("Aborting due to user")
return nil, fmt.Errorf("Aborting due to user")
}
if !eOtherInclude.get("n") && !eOtherInclude.get("none") {
@ -465,7 +546,7 @@ func cleanEditNumberMenu(pkgs []*rpc.Pkg, bases map[string][]*rpc.Pkg, installed
}
}
return toClean, toEdit, nil
return toEdit, nil
}
func cleanBuilds(pkgs []*rpc.Pkg) {
@ -476,20 +557,67 @@ func cleanBuilds(pkgs []*rpc.Pkg) {
}
}
func editPkgBuilds(pkgs []*rpc.Pkg) error {
func showPkgBuildDiffs(pkgs []*rpc.Pkg, bases map[string][]*rpc.Pkg, cloned stringSet) error {
for _, pkg := range pkgs {
dir := filepath.Join(config.BuildDir, pkg.PackageBase)
if shouldUseGit(dir) {
start := "HEAD"
if cloned.get(pkg.PackageBase) {
start = gitEmptyTree
} else {
hasDiff, err := gitHasDiff(config.BuildDir, pkg.PackageBase)
if err != nil {
return err
}
if !hasDiff {
fmt.Printf("%s %s: %s\n", bold(yellow(arrow)), cyan(formatPkgbase(pkg, bases)), bold("No changes -- skipping"))
continue
}
}
args := []string{"diff", start + "..HEAD@{upstream}", "--src-prefix", dir + "/", "--dst-prefix", dir + "/"}
if useColor {
args = append(args, "--color=always")
} else {
args = append(args, "--color=never")
}
err := passToGit(dir, args...)
if err != nil {
return err
}
} else {
editor, editorArgs := editor()
editorArgs = append(editorArgs, filepath.Join(dir, "PKGBUILD"))
editcmd := exec.Command(editor, editorArgs...)
editcmd.Stdin, editcmd.Stdout, editcmd.Stderr = os.Stdin, os.Stdout, os.Stderr
err := editcmd.Run()
if err != nil {
return fmt.Errorf("Editor did not exit successfully, Aborting: %s", err)
}
}
}
return nil
}
func editPkgBuilds(pkgs []*rpc.Pkg, bases map[string][]*rpc.Pkg) error {
pkgbuilds := make([]string, 0, len(pkgs))
for _, pkg := range pkgs {
dir := filepath.Join(config.BuildDir, pkg.PackageBase)
pkgbuilds = append(pkgbuilds, filepath.Join(dir, "PKGBUILD"))
}
editor, editorArgs := editor()
editorArgs = append(editorArgs, pkgbuilds...)
editcmd := exec.Command(editor, editorArgs...)
editcmd.Stdin, editcmd.Stdout, editcmd.Stderr = os.Stdin, os.Stdout, os.Stderr
err := editcmd.Run()
if err != nil {
return fmt.Errorf("Editor did not exit successfully, Aborting: %s", err)
if len(pkgbuilds) > 0 {
editor, editorArgs := editor()
editorArgs = append(editorArgs, pkgbuilds...)
editcmd := exec.Command(editor, editorArgs...)
editcmd.Stdin, editcmd.Stdout, editcmd.Stderr = os.Stdin, os.Stdout, os.Stderr
err := editcmd.Run()
if err != nil {
return fmt.Errorf("Editor did not exit successfully, Aborting: %s", err)
}
}
return nil
@ -530,40 +658,73 @@ func tryParsesrcinfosFile(pkgs []*rpc.Pkg, srcinfos map[string]*gopkg.PKGBUILD,
}
}
func downloadPkgBuilds(pkgs []*rpc.Pkg, targets stringSet, bases map[string][]*rpc.Pkg) error {
for k, pkg := range pkgs {
func pkgBuildsToSkip(pkgs []*rpc.Pkg, targets stringSet) stringSet {
toSkip := make(stringSet)
for _, pkg := range pkgs {
if config.ReDownload == "no" || (config.ReDownload == "yes" && !targets.get(pkg.Name)) {
dir := filepath.Join(config.BuildDir, pkg.PackageBase, ".SRCINFO")
pkgbuild, err := gopkg.ParseSRCINFO(dir)
if err == nil {
version, err := gopkg.NewCompleteVersion(pkg.Version)
if err == nil {
if !version.Newer(pkgbuild.Version()) {
str := bold(cyan("::") + " PKGBUILD up to date, Skipping (%d/%d): %s\n")
fmt.Printf(str, k+1, len(pkgs), cyan(formatPkgbase(pkg, bases)))
continue
versionRPC, errR := gopkg.NewCompleteVersion(pkg.Version)
versionPKG, errP := gopkg.NewCompleteVersion(pkgbuild.Version())
if errP == nil && errR == nil {
if !versionRPC.Newer(versionPKG) {
toSkip.set(pkg.PackageBase)
}
}
}
}
}
return toSkip
}
func mergePkgBuilds(pkgs []*rpc.Pkg) error {
for _, pkg := range pkgs {
if shouldUseGit(filepath.Join(config.BuildDir, pkg.PackageBase)) {
err := gitMerge(baseURL+"/"+pkg.PackageBase+".git", config.BuildDir, pkg.PackageBase)
if err != nil {
return err
}
}
}
return nil
}
func downloadPkgBuilds(pkgs []*rpc.Pkg, bases map[string][]*rpc.Pkg, toSkip stringSet) (stringSet, error) {
cloned := make(stringSet)
for k, pkg := range pkgs {
if toSkip.get(pkg.PackageBase) {
str := bold(cyan("::") + " PKGBUILD up to date, Skipping (%d/%d): %s\n")
fmt.Printf(str, k+1, len(pkgs), cyan(formatPkgbase(pkg, bases)))
continue
}
str := bold(cyan("::") + " Downloading PKGBUILD (%d/%d): %s\n")
fmt.Printf(str, k+1, len(pkgs), cyan(formatPkgbase(pkg, bases)))
var err error
if shouldUseGit(filepath.Join(config.BuildDir, pkg.PackageBase)) {
err = gitDownload(baseURL+"/"+pkg.PackageBase+".git", config.BuildDir, pkg.PackageBase)
clone, err := gitDownload(baseURL+"/"+pkg.PackageBase+".git", config.BuildDir, pkg.PackageBase)
if err != nil {
return nil, err
}
if clone {
cloned.set(pkg.PackageBase)
}
} else {
err = downloadAndUnpack(baseURL+pkg.URLPath, config.BuildDir, false)
}
if err != nil {
return err
err := downloadAndUnpack(baseURL+pkg.URLPath, config.BuildDir)
if err != nil {
return nil, err
}
}
}
return nil
return cloned, nil
}
func downloadPkgBuildsSources(pkgs []*rpc.Pkg, bases map[string][]*rpc.Pkg, incompatible stringSet) (err error) {
@ -584,45 +745,42 @@ func downloadPkgBuildsSources(pkgs []*rpc.Pkg, bases map[string][]*rpc.Pkg, inco
return
}
func buildInstallPkgBuilds(pkgs []*rpc.Pkg, srcinfos map[string]*gopkg.PKGBUILD, targets stringSet, parser *arguments, bases map[string][]*rpc.Pkg, incompatible stringSet) error {
arch, err := alpmHandle.Arch()
if err != nil {
return err
}
for _, pkg := range pkgs {
func buildInstallPkgBuilds(dp *depPool, do *depOrder, srcinfos map[string]*gopkg.PKGBUILD, parser *arguments, incompatible stringSet) error {
for _, pkg := range do.Aur {
dir := filepath.Join(config.BuildDir, pkg.PackageBase)
built := true
srcinfo := srcinfos[pkg.PackageBase]
args := []string{"--nobuild", "-fC"}
if incompatible.get(pkg.PackageBase) {
args = append(args, "--ignorearch")
}
//pkgver bump
err := passToMakepkg(dir, "--nobuild", "-fCc")
err := passToMakepkg(dir, args...)
if err != nil {
return fmt.Errorf("Error making: %s", pkg.Name)
}
version, err := getVersionFromPkgbuild(dir)
pkgdests, version, err := parsePackageList(dir)
if err != nil {
return err
}
if config.ReBuild == "no" || (config.ReBuild == "yes" && !targets.get(pkg.Name)) {
for _, split := range bases[pkg.PackageBase] {
file, err := completeFileName(dir, split.Name+"-"+version+"-"+arch+".pkg")
if err != nil {
return err
if config.ReBuild == "no" || (config.ReBuild == "yes" && !dp.Explicit.get(pkg.Name)) {
for _, split := range do.Bases[pkg.PackageBase] {
pkgdest, ok := pkgdests[split.Name]
if !ok {
return fmt.Errorf("Could not find PKGDEST for: %s", split.Name)
}
if file == "" {
file, err = completeFileName(dir, split.Name+"-"+version+"-"+"any"+".pkg")
if err != nil {
return err
}
}
if file == "" {
_, err := os.Stat(pkgdest)
if os.IsNotExist(err) {
built = false
} else if err != nil {
return err
}
}
} else {
@ -631,9 +789,9 @@ func buildInstallPkgBuilds(pkgs []*rpc.Pkg, srcinfos map[string]*gopkg.PKGBUILD,
if built {
fmt.Println(bold(yellow(arrow)),
cyan(pkg.Name+"-"+pkg.Version)+bold(" Already made -- skipping build"))
cyan(pkg.Name+"-"+version)+bold(" Already made -- skipping build"))
} else {
args := []string{"-Ccf", "--noconfirm"}
args := []string{"-cf", "--noconfirm", "--noextract", "--noprepare", "--holdver"}
if incompatible.get(pkg.PackageBase) {
args = append(args, "--ignorearch")
@ -646,7 +804,7 @@ func buildInstallPkgBuilds(pkgs []*rpc.Pkg, srcinfos map[string]*gopkg.PKGBUILD,
}
arguments := parser.copy()
arguments.targets = make(stringSet)
arguments.clearTargets()
arguments.op = "U"
arguments.delArg("confirm")
arguments.delArg("c", "clean")
@ -658,6 +816,8 @@ func buildInstallPkgBuilds(pkgs []*rpc.Pkg, srcinfos map[string]*gopkg.PKGBUILD,
depArguments := makeArguments()
depArguments.addArg("D", "asdeps")
expArguments := makeArguments()
expArguments.addArg("D", "asexplicit")
//remotenames: names of all non repo packages on the system
_, _, localNames, remoteNames, err := filterPackages()
@ -670,28 +830,24 @@ func buildInstallPkgBuilds(pkgs []*rpc.Pkg, srcinfos map[string]*gopkg.PKGBUILD,
remoteNamesCache := sliceToStringSet(remoteNames)
localNamesCache := sliceToStringSet(localNames)
for _, split := range bases[pkg.PackageBase] {
file, err := completeFileName(dir, split.Name+"-"+version+"-"+arch+".pkg")
if err != nil {
return err
for _, split := range do.Bases[pkg.PackageBase] {
pkgdest, ok := pkgdests[split.Name]
if !ok {
return fmt.Errorf("Could not find PKGDEST for: %s", split.Name)
}
if file == "" {
file, err = completeFileName(dir, split.Name+"-"+version+"-"+"any"+".pkg")
if err != nil {
return err
}
}
if file == "" {
return fmt.Errorf("Could not find built package " + split.Name + "-" + version + "-" + arch + ".pkg")
}
arguments.addTarget(file)
//if !targets.get(split.Name) {
if !targets.get(split.Name) && !localNamesCache.get(split.Name) && !remoteNamesCache.get(split.Name) {
arguments.addTarget(pkgdest)
if !dp.Explicit.get(split.Name) && !localNamesCache.get(split.Name) && !remoteNamesCache.get(split.Name) {
depArguments.addTarget(split.Name)
}
if dp.Explicit.get(split.Name) {
if parser.existsArg("asdeps", "asdep") {
depArguments.addTarget(split.Name)
} else if parser.existsArg("asexplicit", "asexp") {
expArguments.addTarget(split.Name)
}
}
}
oldConfirm := config.NoConfirm
@ -701,7 +857,7 @@ func buildInstallPkgBuilds(pkgs []*rpc.Pkg, srcinfos map[string]*gopkg.PKGBUILD,
return err
}
for _, pkg := range bases[pkg.PackageBase] {
for _, pkg := range do.Bases[pkg.PackageBase] {
updateVCSData(pkg.Name, srcinfo.Source)
}

View File

@ -2,6 +2,7 @@ package main
import (
"bytes"
"context"
"fmt"
"io/ioutil"
"net/http"
@ -36,10 +37,6 @@ func newPkg(basename string) *rpc.Pkg {
return &rpc.Pkg{Name: basename, PackageBase: basename}
}
func newSplitPkg(basename, name string) *rpc.Pkg {
return &rpc.Pkg{Name: name, PackageBase: basename}
}
func getPgpKey(key string) string {
var buffer bytes.Buffer
@ -73,7 +70,7 @@ func TestImportKeys(t *testing.T) {
config.GpgFlags = fmt.Sprintf("--homedir %s --keyserver 127.0.0.1", keyringDir)
server := startPgpKeyServer()
defer server.Shutdown(nil)
defer server.Shutdown(context.TODO())
casetests := []struct {
keys []string
@ -138,7 +135,7 @@ func TestCheckPgpKeys(t *testing.T) {
config.GpgFlags = fmt.Sprintf("--homedir %s --keyserver 127.0.0.1", keyringDir)
server := startPgpKeyServer()
defer server.Shutdown(nil)
defer server.Shutdown(context.TODO())
casetests := []struct {
pkgs []*rpc.Pkg

53
main.go
View File

@ -10,29 +10,39 @@ import (
alpm "github.com/jguer/go-alpm"
)
func initPaths() {
if configHome = os.Getenv("XDG_CONFIG_HOME"); configHome != "" {
if info, err := os.Stat(configHome); err == nil && info.IsDir() {
configHome = configHome + "/yay"
} else {
configHome = filepath.Join(os.Getenv("HOME"), ".config/yay")
func setPaths() error {
if _configHome, set := os.LookupEnv("XDG_CONFIG_HOME"); set {
if _configHome == "" {
return fmt.Errorf("XDG_CONFIG_HOME set but empty")
}
configHome = filepath.Join(_configHome, "yay")
} else if _configHome, set := os.LookupEnv("HOME"); set {
if _configHome == "" {
return fmt.Errorf("HOME set but empty")
}
configHome = filepath.Join(_configHome, ".config/yay")
} else {
configHome = filepath.Join(os.Getenv("HOME"), ".config/yay")
return fmt.Errorf("XDG_CONFIG_HOME and HOME unset")
}
if cacheHome = os.Getenv("XDG_CACHE_HOME"); cacheHome != "" {
if info, err := os.Stat(cacheHome); err == nil && info.IsDir() {
cacheHome = filepath.Join(cacheHome, "yay")
} else {
cacheHome = filepath.Join(os.Getenv("HOME"), ".cache/yay")
if _cacheHome, set := os.LookupEnv("XDG_CACHE_HOME"); set {
if _cacheHome == "" {
return fmt.Errorf("XDG_CACHE_HOME set but empty")
}
cacheHome = filepath.Join(_cacheHome, "yay")
} else if _cacheHome, set := os.LookupEnv("HOME"); set {
if _cacheHome == "" {
return fmt.Errorf("XDG_CACHE_HOME set but empty")
}
cacheHome = filepath.Join(_cacheHome, ".cache/yay")
} else {
cacheHome = filepath.Join(os.Getenv("HOME"), "/.cache/yay")
return fmt.Errorf("XDG_CACHE_HOME and HOME unset")
}
configFile = filepath.Join(configHome, configFileName)
vcsFile = filepath.Join(cacheHome, vcsFileName)
return nil
}
func initConfig() (err error) {
@ -60,6 +70,14 @@ func initConfig() (err error) {
err)
defaultSettings(&config)
}
if _, err = os.Stat(config.BuildDir); os.IsNotExist(err) {
err = os.MkdirAll(config.BuildDir, 0755)
if err != nil {
err = fmt.Errorf("Unable to create BuildDir directory:\n%s\n"+
"The error was:\n%s", config.BuildDir, err)
return
}
}
}
}
@ -176,7 +194,12 @@ func main() {
goto cleanup
}
initPaths()
err = setPaths()
if err != nil {
fmt.Println(err)
status = 1
goto cleanup
}
err = initConfig()
if err != nil {
@ -212,7 +235,7 @@ func main() {
cleanup:
//cleanup
//from here on out dont exit if an error occurs
//from here on out don't exit if an error occurs
//if we fail to save the configuration
//at least continue on and try clean up other parts

130
parser.go
View File

@ -1,7 +1,9 @@
package main
import (
"bytes"
"fmt"
"html"
"io"
"os"
"strconv"
@ -13,7 +15,7 @@ import (
// Other types of sets are used throughout the code but do not have
// their own typedef.
// String sets and <type>sets should be used throughout the code when applicable,
// they are a lot more flexable than slices and provide easy lookup.
// they are a lot more flexible than slices and provide easy lookup.
type stringSet map[string]struct{}
func (set stringSet) set(v string) {
@ -39,6 +41,16 @@ func (set stringSet) toSlice() []string {
return slice
}
func (set stringSet) copy() stringSet {
newSet := make(stringSet)
for str := range set {
newSet.set(str)
}
return newSet
}
func sliceToStringSet(in []string) stringSet {
set := make(stringSet)
@ -60,7 +72,7 @@ type arguments struct {
options map[string]string
globals map[string]string
doubles stringSet // Tracks args passed twice such as -yy and -dd
targets stringSet
targets []string
}
func makeArguments() *arguments {
@ -69,7 +81,7 @@ func makeArguments() *arguments {
make(map[string]string),
make(map[string]string),
make(stringSet),
make(stringSet),
make([]string, 0),
}
}
@ -86,9 +98,8 @@ func (parser *arguments) copy() (cp *arguments) {
cp.globals[k] = v
}
for k, v := range parser.targets {
cp.targets[k] = v
}
cp.targets = make([]string, len(parser.targets))
copy(cp.targets, parser.targets)
for k, v := range parser.doubles {
cp.doubles[k] = v
@ -220,9 +231,12 @@ func (parser *arguments) getArg(options ...string) (arg string, double bool, exi
existCount := 0
for _, option := range options {
arg, exists = parser.options[option]
var value string
value, exists = parser.options[option]
if exists {
arg = value
existCount++
_, exists = parser.doubles[option]
@ -232,9 +246,10 @@ func (parser *arguments) getArg(options ...string) (arg string, double bool, exi
}
arg, exists = parser.globals[option]
value, exists = parser.globals[option]
if exists {
arg = value
existCount++
_, exists = parser.doubles[option]
@ -252,15 +267,11 @@ func (parser *arguments) getArg(options ...string) (arg string, double bool, exi
}
func (parser *arguments) addTarget(targets ...string) {
for _, target := range targets {
parser.targets[target] = struct{}{}
}
parser.targets = append(parser.targets, targets...)
}
func (parser *arguments) delTarget(targets ...string) {
for _, target := range targets {
delete(parser.targets, target)
}
func (parser *arguments) clearTargets() {
parser.targets = make([]string, 0)
}
// Multiple args acts as an OR operator
@ -275,14 +286,6 @@ func (parser *arguments) existsDouble(options ...string) bool {
return false
}
func (parser *arguments) formatTargets() (args []string) {
for target := range parser.targets {
args = append(args, target)
}
return
}
func (parser *arguments) formatArgs() (args []string) {
var op string
@ -297,15 +300,15 @@ func (parser *arguments) formatArgs() (args []string) {
continue
}
formatedOption := formatArg(option)
args = append(args, formatedOption)
formattedOption := formatArg(option)
args = append(args, formattedOption)
if hasParam(option) {
args = append(args, arg)
}
if parser.existsDouble(option) {
args = append(args, formatedOption)
args = append(args, formattedOption)
}
}
@ -314,15 +317,15 @@ func (parser *arguments) formatArgs() (args []string) {
func (parser *arguments) formatGlobals() (args []string) {
for option, arg := range parser.globals {
formatedOption := formatArg(option)
args = append(args, formatedOption)
formattedOption := formatArg(option)
args = append(args, formattedOption)
if hasParam(option) {
args = append(args, arg)
}
if parser.existsDouble(option) {
args = append(args, formatedOption)
args = append(args, formattedOption)
}
}
@ -466,6 +469,8 @@ func hasParam(arg string) bool {
return true
case "answerclean":
return true
case "answerdiff":
return true
case "answeredit":
return true
case "answerupgrade":
@ -619,8 +624,8 @@ func (parser *arguments) parseCommandLine() (err error) {
//of course the implementation is up to the caller, this function mearley parses
//the input and organizes it
func parseNumberMenu(input string) (intRanges, intRanges, stringSet, stringSet) {
include := make(intRanges, 0, 0)
exclude := make(intRanges, 0, 0)
include := make(intRanges, 0)
exclude := make(intRanges, 0)
otherInclude := make(stringSet)
otherExclude := make(stringSet)
@ -669,3 +674,66 @@ func parseNumberMenu(input string) (intRanges, intRanges, stringSet, stringSet)
return include, exclude, otherInclude, otherExclude
}
// Crude html parsing, good enough for the arch news
// This is only displayed in the terminal so there should be no security
// concerns
func parseNews(str string) string {
var buffer bytes.Buffer
var tagBuffer bytes.Buffer
var escapeBuffer bytes.Buffer
inTag := false
inEscape := false
for _, char := range str {
if inTag {
if char == '>' {
inTag = false
switch tagBuffer.String() {
case "code":
buffer.WriteString(cyanCode)
case "/code":
buffer.WriteString(resetCode)
case "/p":
buffer.WriteRune('\n')
}
continue
}
tagBuffer.WriteRune(char)
continue
}
if inEscape {
if char == ';' {
inEscape = false
escapeBuffer.WriteRune(char)
s := html.UnescapeString(escapeBuffer.String())
buffer.WriteString(s)
continue
}
escapeBuffer.WriteRune(char)
continue
}
if char == '<' {
inTag = true
tagBuffer.Reset()
continue
}
if char == '&' {
inEscape = true
escapeBuffer.Reset()
escapeBuffer.WriteRune(char)
continue
}
buffer.WriteRune(char)
}
buffer.WriteString(resetCode)
return buffer.String()
}

View File

@ -19,7 +19,7 @@ func intRangesEqual(a, b intRanges) bool {
r1 := a[n]
r2 := b[n]
if r1.min != r1.min || r1.max != r2.max {
if r1.min != r2.min || r1.max != r2.max {
return false
}
}

274
print.go
View File

@ -1,7 +1,12 @@
package main
import (
"bufio"
"bytes"
"encoding/xml"
"fmt"
"io/ioutil"
"net/http"
"os"
"strconv"
"strings"
@ -13,7 +18,7 @@ import (
const arrow = "==>"
const smallArrow = " ->"
func (warnings *aurWarnings) Print() {
func (warnings *aurWarnings) print() {
if len(warnings.Missing) > 0 {
fmt.Print(bold(yellow(smallArrow)) + " Missing AUR Packages:")
for _, name := range warnings.Missing {
@ -153,23 +158,38 @@ func formatPkgbase(pkg *rpc.Pkg, bases map[string][]*rpc.Pkg) string {
return str
}
func (u upgrade) StylizedNameWithRepository() string {
return bold(colourHash(u.Repository)) + "/" + bold(u.Name)
}
// Print prints the details of the packages to upgrade.
func (u upSlice) Print(start int) {
func (u upSlice) print() {
longestName, longestVersion := 0, 0
for _, pack := range u {
packNameLen := len(pack.StylizedNameWithRepository())
version, _ := getVersionDiff(pack.LocalVersion, pack.RemoteVersion)
packVersionLen := len(version)
longestName = max(packNameLen, longestName)
longestVersion = max(packVersionLen, longestVersion)
}
namePadding := fmt.Sprintf("%%-%ds ", longestName)
versionPadding := fmt.Sprintf("%%-%ds", longestVersion)
numberPadding := fmt.Sprintf("%%%dd ", len(fmt.Sprintf("%v", len(u))))
for k, i := range u {
left, right := getVersionDiff(i.LocalVersion, i.RemoteVersion)
fmt.Print(magenta(fmt.Sprintf("%3d ", len(u)+start-k-1)))
fmt.Print(bold(colourHash(i.Repository)), "/", bold(i.Name))
fmt.Print(magenta(fmt.Sprintf(numberPadding, len(u)-k)))
w := 70 - len(i.Repository) - len(i.Name)
padding := fmt.Sprintf("%%%ds", w)
fmt.Printf(padding, left)
fmt.Printf(" -> %s\n", right)
fmt.Printf(namePadding, i.StylizedNameWithRepository())
fmt.Printf("%s -> %s\n", fmt.Sprintf(versionPadding, left), right)
}
}
// printDownloadsFromRepo prints repository packages to be downloaded
func printDepCatagories(dc *depCatagories) {
func (do *depOrder) Print() {
repo := ""
repoMake := ""
aur := ""
@ -180,47 +200,47 @@ func printDepCatagories(dc *depCatagories) {
aurLen := 0
aurMakeLen := 0
for _, pkg := range dc.Repo {
if dc.MakeOnly.get(pkg.Name()) {
repoMake += " " + pkg.Name() + "-" + pkg.Version()
repoMakeLen++
} else {
for _, pkg := range do.Repo {
if do.Runtime.get(pkg.Name()) {
repo += " " + pkg.Name() + "-" + pkg.Version()
repoLen++
} else {
repoMake += " " + pkg.Name() + "-" + pkg.Version()
repoMakeLen++
}
}
for _, pkg := range dc.Aur {
for _, pkg := range do.Aur {
pkgStr := " " + pkg.PackageBase + "-" + pkg.Version
pkgStrMake := pkgStr
push := false
pushMake := false
if len(dc.Bases[pkg.PackageBase]) > 1 || pkg.PackageBase != pkg.Name {
if len(do.Bases[pkg.PackageBase]) > 1 || pkg.PackageBase != pkg.Name {
pkgStr += " ("
pkgStrMake += " ("
for _, split := range dc.Bases[pkg.PackageBase] {
if dc.MakeOnly.get(split.Name) {
pkgStrMake += split.Name + " "
aurMakeLen++
pushMake = true
} else {
for _, split := range do.Bases[pkg.PackageBase] {
if do.Runtime.get(split.Name) {
pkgStr += split.Name + " "
aurLen++
push = true
} else {
pkgStrMake += split.Name + " "
aurMakeLen++
pushMake = true
}
}
pkgStr = pkgStr[:len(pkgStr)-1] + ")"
pkgStrMake = pkgStrMake[:len(pkgStrMake)-1] + ")"
} else if dc.MakeOnly.get(pkg.Name) {
aurMakeLen++
pushMake = true
} else {
} else if do.Runtime.get(pkg.Name) {
aurLen++
push = true
} else {
aurMakeLen++
pushMake = true
}
if push {
@ -247,25 +267,31 @@ func printDownloads(repoName string, length int, packages string) {
fmt.Println(repoInfo + cyan(packages))
}
func printInfoValue(str, value string) {
fmt.Printf(bold("%-16s%s")+" %s\n", str, ":", value)
}
// PrintInfo prints package info like pacman -Si.
func PrintInfo(a *rpc.Pkg) {
fmt.Println(bold("Repository :"), "aur")
fmt.Println(bold("Name :"), a.Name)
fmt.Println(bold("Version :"), a.Version)
fmt.Println(bold("Description :"), a.Description)
fmt.Println(bold("URL :"), a.URL)
fmt.Println(bold("Licenses :"), strings.Join(a.License, " "))
fmt.Println(bold("Provides :"), strings.Join(a.Provides, " "))
fmt.Println(bold("Depends On :"), strings.Join(a.Depends, " "))
fmt.Println(bold("Make Deps :"), strings.Join(a.MakeDepends, " "))
fmt.Println(bold("Check Deps :"), strings.Join(a.CheckDepends, " "))
fmt.Println(bold("Optional Deps :"), strings.Join(a.OptDepends, " "))
fmt.Println(bold("Conflicts With :"), strings.Join(a.Conflicts, " "))
fmt.Println(bold("Maintainer :"), a.Maintainer)
fmt.Println(bold("Votes :"), a.NumVotes)
fmt.Println(bold("Popularity :"), a.Popularity)
printInfoValue("Repository", "aur")
printInfoValue("Name", a.Name)
printInfoValue("Version", a.Version)
printInfoValue("Description", a.Description)
printInfoValue("URL", a.URL)
printInfoValue("Licenses", strings.Join(a.License, " "))
printInfoValue("Provides", strings.Join(a.Provides, " "))
printInfoValue("Depends On", strings.Join(a.Depends, " "))
printInfoValue("Make Deps", strings.Join(a.MakeDepends, " "))
printInfoValue("Check Deps", strings.Join(a.CheckDepends, " "))
printInfoValue("Optional Deps", strings.Join(a.OptDepends, " "))
printInfoValue("Conflicts With", strings.Join(a.Conflicts, " "))
printInfoValue("Maintainer", a.Maintainer)
printInfoValue("Votes", fmt.Sprintf("%d", a.NumVotes))
printInfoValue("Popularity", fmt.Sprintf("%f", a.Popularity))
if a.OutOfDate != 0 {
fmt.Println(bold("Out-of-date :"), "Yes", "["+formatTime(a.OutOfDate)+"]")
printInfoValue("Out-of-date", "Yes ["+formatTime(a.OutOfDate)+"]")
} else {
printInfoValue("Out-of-date", "No")
}
fmt.Println()
@ -337,32 +363,37 @@ func printNumberOfUpdates() error {
//TODO: Make it less hacky
func printUpdateList(parser *arguments) error {
targets := sliceToStringSet(parser.targets)
warnings := &aurWarnings{}
old := os.Stdout // keep backup of the real stdout
os.Stdout = nil
_, _, localNames, remoteNames, err := filterPackages()
if err != nil {
return err
}
aurUp, repoUp, err := upList(warnings)
os.Stdout = old // restoring the real stdout
if err != nil {
return err
}
noTargets := len(parser.targets) == 0
noTargets := len(targets) == 0
if !parser.existsArg("m", "foreigne") {
if !parser.existsArg("m", "foreign") {
for _, pkg := range repoUp {
if noTargets || parser.targets.get(pkg.Name) {
if noTargets || targets.get(pkg.Name) {
fmt.Printf("%s %s -> %s\n", bold(pkg.Name), green(pkg.LocalVersion), green(pkg.RemoteVersion))
delete(parser.targets, pkg.Name)
delete(targets, pkg.Name)
}
}
}
if !parser.existsArg("n", "native") {
for _, pkg := range aurUp {
if noTargets || parser.targets.get(pkg.Name) {
if noTargets || targets.get(pkg.Name) {
fmt.Printf("%s %s -> %s\n", bold(pkg.Name), green(pkg.LocalVersion), green(pkg.RemoteVersion))
delete(parser.targets, pkg.Name)
delete(targets, pkg.Name)
}
}
}
@ -370,7 +401,7 @@ func printUpdateList(parser *arguments) error {
missing := false
outer:
for pkg := range parser.targets {
for pkg := range targets {
for _, name := range localNames {
if name == pkg {
continue outer
@ -394,10 +425,93 @@ outer:
return nil
}
// Formats a unix timestamp to yyyy/mm/dd
type item struct {
Title string `xml:"title"`
Link string `xml:"link"`
Description string `xml:"description"`
PubDate string `xml:"pubDate"`
Creator string `xml:"dc:creator"`
}
func (item item) print(buildTime time.Time) {
var fd string
date, err := time.Parse(time.RFC1123Z, item.PubDate)
if err != nil {
fmt.Println(err)
} else {
fd = formatTime(int(date.Unix()))
if _, double, _ := cmdArgs.getArg("news", "w"); !double && !buildTime.IsZero() {
if buildTime.After(date) {
return
}
}
}
fmt.Println(bold(magenta(fd)), bold(strings.TrimSpace(item.Title)))
//fmt.Println(strings.TrimSpace(item.Link))
if !cmdArgs.existsArg("q", "quiet") {
desc := strings.TrimSpace(parseNews(item.Description))
fmt.Println(desc)
}
}
type channel struct {
Title string `xml:"title"`
Link string `xml:"link"`
Description string `xml:"description"`
Language string `xml:"language"`
Lastbuilddate string `xml:"lastbuilddate"`
Items []item `xml:"item"`
}
type rss struct {
Channel channel `xml:"channel"`
}
func printNewsFeed() error {
resp, err := http.Get("https://archlinux.org/feeds/news")
if err != nil {
return err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
rss := rss{}
d := xml.NewDecoder(bytes.NewReader(body))
err = d.Decode(&rss)
if err != nil {
return err
}
buildTime, err := lastBuildTime()
if err != nil {
return err
}
if config.SortMode == BottomUp {
for i := len(rss.Channel.Items) - 1; i >= 0; i-- {
rss.Channel.Items[i].print(buildTime)
}
} else {
for i := 0; i < len(rss.Channel.Items); i++ {
rss.Channel.Items[i].print(buildTime)
}
}
return nil
}
// Formats a unix timestamp to ISO 8601 date (yyyy-mm-dd)
func formatTime(i int) string {
t := time.Unix(int64(i), 0)
return fmt.Sprintf("%d/%02d/%02d", t.Year(), int(t.Month()), t.Day())
return t.Format("2006-01-02")
}
const (
@ -460,3 +574,61 @@ func colourHash(name string) (output string) {
}
return fmt.Sprintf("\x1b[%dm%s\x1b[0m", hash%6+31, name)
}
func providerMenu(dep string, providers providers) *rpc.Pkg {
size := providers.Len()
fmt.Print(bold(cyan(":: ")))
str := bold(fmt.Sprintf(bold("There are %d providers available for %s:"), size, dep))
size = 1
str += bold(cyan("\n:: ")) + bold("Repository AUR\n ")
for _, pkg := range providers.Pkgs {
str += fmt.Sprintf("%d) %s ", size, pkg.Name)
size++
}
fmt.Println(str)
for {
fmt.Print("\nEnter a number (default=1): ")
if config.NoConfirm {
fmt.Println()
break
}
reader := bufio.NewReader(os.Stdin)
numberBuf, overflow, err := reader.ReadLine()
if err != nil {
fmt.Println(err)
break
}
if overflow {
fmt.Println("Input too long")
continue
}
if string(numberBuf) == "" {
return providers.Pkgs[0]
}
num, err := strconv.Atoi(string(numberBuf))
if err != nil {
fmt.Printf("%s invalid number: %s\n", red("error:"), string(numberBuf))
continue
}
if num < 1 || num > size {
fmt.Printf("%s invalid value: %d is not between %d and %d\n", red("error:"), num, 1, size)
continue
}
return providers.Pkgs[num-1]
}
return nil
}

View File

@ -5,6 +5,7 @@ import (
"sort"
"strings"
"sync"
"time"
alpm "github.com/jguer/go-alpm"
rpc "github.com/mikkeloscar/aur"
@ -158,23 +159,41 @@ func narrowSearch(pkgS []string, sortS bool) (aurQuery, error) {
// SyncSearch presents a query to the local repos and to the AUR.
func syncSearch(pkgS []string) (err error) {
aq, aurErr := narrowSearch(pkgS, true)
pq, _, err := queryRepo(pkgS)
if err != nil {
return err
pkgS = removeInvalidTargets(pkgS)
var aurErr error
var repoErr error
var aq aurQuery
var pq repoQuery
if mode == ModeAUR || mode == ModeAny {
aq, aurErr = narrowSearch(pkgS, true)
}
if mode == ModeRepo || mode == ModeAny {
pq, _, repoErr = queryRepo(pkgS)
if repoErr != nil {
return err
}
}
if config.SortMode == BottomUp {
aq.printSearch(1)
pq.printSearch()
if mode == ModeAUR || mode == ModeAny {
aq.printSearch(1)
}
if mode == ModeRepo || mode == ModeAny {
pq.printSearch()
}
} else {
pq.printSearch()
aq.printSearch(1)
if mode == ModeRepo || mode == ModeAny {
pq.printSearch()
}
if mode == ModeAUR || mode == ModeAny {
aq.printSearch(1)
}
}
if aurErr != nil {
fmt.Printf("Error during AUR search: %s\n", aurErr)
fmt.Println("Showing Repo packags only")
fmt.Println("Showing Repo packages only")
}
return nil
@ -183,6 +202,8 @@ func syncSearch(pkgS []string) (err error) {
// SyncInfo serves as a pacman -Si for repo packages and AUR packages.
func syncInfo(pkgS []string) (err error) {
var info []*rpc.Pkg
missing := false
pkgS = removeInvalidTargets(pkgS)
aurS, repoS, err := packageSlices(pkgS)
if err != nil {
return
@ -198,6 +219,7 @@ func syncInfo(pkgS []string) (err error) {
info, err = aurInfoPrint(noDb)
if err != nil {
missing = true
fmt.Println(err)
}
}
@ -205,7 +227,8 @@ func syncInfo(pkgS []string) (err error) {
// Repo always goes first
if len(repoS) != 0 {
arguments := cmdArgs.copy()
arguments.delTarget(aurS...)
arguments.clearTargets()
arguments.addTarget(repoS...)
err = passToPacman(arguments)
if err != nil {
@ -213,12 +236,20 @@ func syncInfo(pkgS []string) (err error) {
}
}
if len(aurS) != 0 {
if len(aurS) != len(info) {
missing = true
}
if len(info) != 0 {
for _, pkg := range info {
PrintInfo(pkg)
}
}
if missing {
err = fmt.Errorf("")
}
return
}
@ -283,10 +314,10 @@ func packageSlices(toCheck []string) (aur []string, repo []string, err error) {
db, name := splitDbFromName(_pkg)
found := false
if db == "aur" {
if db == "aur" || mode == ModeAUR {
aur = append(aur, _pkg)
continue
} else if db != "" {
} else if db != "" || mode == ModeRepo {
repo = append(repo, _pkg)
continue
}
@ -332,7 +363,7 @@ func hangingPackages(removeOptional bool) (hanging []string, err error) {
// State = 2 - Keep package and have iterated over dependencies
safePackages := make(map[string]uint8)
// provides stores a mapping from the provides name back to the original package name
provides := make(map[string]stringSet)
provides := make(mapStringSet)
packages := localDb.PkgCache()
// Mark explicit dependencies and enumerate the provides list
@ -344,7 +375,7 @@ func hangingPackages(removeOptional bool) (hanging []string, err error) {
}
pkg.Provides().ForEach(func(dep alpm.Depend) error {
addMapStringSet(provides, dep.Name, pkg.Name())
provides.Add(dep.Name, pkg.Name())
return nil
})
return nil
@ -353,7 +384,7 @@ func hangingPackages(removeOptional bool) (hanging []string, err error) {
iterateAgain := true
processDependencies := func(pkg alpm.Package) error {
if state, _ := safePackages[pkg.Name()]; state == 0 || state == 2 {
if state := safePackages[pkg.Name()]; state == 0 || state == 2 {
return nil
}
@ -407,6 +438,24 @@ func hangingPackages(removeOptional bool) (hanging []string, err error) {
return
}
func lastBuildTime() (time.Time, error) {
var time time.Time
pkgs, _, _, _, err := filterPackages()
if err != nil {
return time, err
}
for _, pkg := range pkgs {
thisTime := pkg.BuildDate()
if thisTime.After(time) {
time = thisTime
}
}
return time, nil
}
// Statistics returns statistics about packages installed in system
func statistics() (info struct {
Totaln int
@ -516,7 +565,7 @@ func aurInfoPrint(names []string) ([]*rpc.Pkg, error) {
return info, err
}
warnings.Print()
warnings.print()
return info, nil
}

View File

@ -138,31 +138,39 @@ func upList(warnings *aurWarnings) (aurUp upSlice, repoUp upSlice, err error) {
var aurErr error
var develErr error
fmt.Println(bold(cyan("::") + bold(" Searching databases for updates...")))
wg.Add(1)
go func() {
repoUp, repoErr = upRepo(local)
wg.Done()
}()
pkgdata := make(map[string]*rpc.Pkg)
fmt.Println(bold(cyan("::") + bold(" Searching AUR for updates...")))
wg.Add(1)
go func() {
aurUp, aurErr = upAUR(remote, remoteNames, warnings)
wg.Done()
}()
if config.Devel {
fmt.Println(bold(cyan("::") + bold(" Checking development packages...")))
if mode == ModeAny || mode == ModeRepo {
fmt.Println(bold(cyan("::") + bold(" Searching databases for updates...")))
wg.Add(1)
go func() {
develUp, develErr = upDevel(remote)
repoUp, repoErr = upRepo(local)
wg.Done()
}()
}
if mode == ModeAny || mode == ModeAUR {
fmt.Println(bold(cyan("::") + bold(" Searching AUR for updates...")))
wg.Add(1)
go func() {
aurUp, aurErr = upAUR(remote, remoteNames, pkgdata, warnings)
wg.Done()
}()
if config.Devel {
fmt.Println(bold(cyan("::") + bold(" Checking development packages...")))
wg.Add(1)
go func() {
develUp, develErr = upDevel(remote)
wg.Done()
}()
}
}
wg.Wait()
printLocalNewerThanAUR(remote, pkgdata)
errs := make([]string, 0)
for _, e := range []error{repoErr, aurErr, develErr} {
if e != nil {
@ -193,8 +201,8 @@ func upList(warnings *aurWarnings) (aurUp upSlice, repoUp upSlice, err error) {
}
func upDevel(remote []alpm.Package) (toUpgrade upSlice, err error) {
toUpdate := make([]alpm.Package, 0, 0)
toRemove := make([]string, 0, 0)
toUpdate := make([]alpm.Package, 0)
toRemove := make([]string, 0)
var mux1 sync.Mutex
var mux2 sync.Mutex
@ -228,9 +236,7 @@ func upDevel(remote []alpm.Package) (toUpgrade upSlice, err error) {
for _, pkg := range toUpdate {
if pkg.ShouldIgnore() {
left, right := getVersionDiff(pkg.Version(), "latest-commit")
fmt.Print(yellow(bold(smallArrow)))
fmt.Printf(" Ignoring package upgrade %s (%s => %s)\n", cyan(pkg.Name()), left, right)
printIgnoringPackage(pkg, "latest-commit")
} else {
toUpgrade = append(toUpgrade, upgrade{pkg.Name(), "devel", pkg.Version(), "latest-commit"})
}
@ -242,14 +248,16 @@ func upDevel(remote []alpm.Package) (toUpgrade upSlice, err error) {
// upAUR gathers foreign packages and checks if they have new versions.
// Output: Upgrade type package list.
func upAUR(remote []alpm.Package, remoteNames []string, warnings *aurWarnings) (upSlice, error) {
func upAUR(
remote []alpm.Package, remoteNames []string,
pkgdata map[string]*rpc.Pkg, warnings *aurWarnings) (upSlice, error) {
toUpgrade := make(upSlice, 0)
_pkgdata, err := aurInfo(remoteNames, warnings)
if err != nil {
return nil, err
}
pkgdata := make(map[string]*rpc.Pkg)
for _, pkg := range _pkgdata {
pkgdata[pkg.Name] = pkg
}
@ -263,9 +271,7 @@ func upAUR(remote []alpm.Package, remoteNames []string, warnings *aurWarnings) (
if (config.TimeUpdate && (int64(aurPkg.LastModified) > pkg.BuildDate().Unix())) ||
(alpm.VerCmp(pkg.Version(), aurPkg.Version) < 0) {
if pkg.ShouldIgnore() {
left, right := getVersionDiff(pkg.Version(), aurPkg.Version)
fmt.Print(yellow(bold(smallArrow)))
fmt.Printf(" Ignoring package upgrade: %s (%s => %s)\n", cyan(pkg.Name()), left, right)
printIgnoringPackage(pkg, aurPkg.Version)
} else {
toUpgrade = append(toUpgrade, upgrade{aurPkg.Name, "aur", pkg.Version(), aurPkg.Version})
}
@ -275,6 +281,35 @@ func upAUR(remote []alpm.Package, remoteNames []string, warnings *aurWarnings) (
return toUpgrade, nil
}
func printIgnoringPackage(pkg alpm.Package, newPkgVersion string) {
left, right := getVersionDiff(pkg.Version(), newPkgVersion)
fmt.Println(
yellow(bold(smallArrow)) + fmt.Sprintf(
" Ignoring package upgrade: %s (%s -> %s)",
cyan(pkg.Name()), left, right))
}
func printLocalNewerThanAUR(
remote []alpm.Package, pkgdata map[string]*rpc.Pkg) {
for _, pkg := range remote {
aurPkg, ok := pkgdata[pkg.Name()]
if !ok {
continue
}
left, right := getVersionDiff(pkg.Version(), aurPkg.Version)
if !isDevelName(pkg.Name()) &&
alpm.VerCmp(pkg.Version(), aurPkg.Version) > 0 {
fmt.Println(
yellow(bold(smallArrow)) + fmt.Sprintf(
" Local package is newer than AUR: %s (%s -> %s)",
cyan(pkg.Name()), left, right))
}
}
}
// upRepo gathers local packages and checks if they have new versions.
// Output: Upgrade type package list.
func upRepo(local []alpm.Package) (upSlice, error) {
@ -289,9 +324,7 @@ func upRepo(local []alpm.Package) (upSlice, error) {
newPkg := pkg.NewVersion(dbList)
if newPkg != nil {
if pkg.ShouldIgnore() {
left, right := getVersionDiff(pkg.Version(), newPkg.Version())
fmt.Print(yellow(bold(smallArrow)))
fmt.Printf(" Ignoring package upgrade: %s (%s => %s)\n", cyan(pkg.Name()), left, right)
printIgnoringPackage(pkg, newPkg.Version())
} else {
slice = append(slice, upgrade{pkg.Name(), newPkg.DB().Name(), pkg.Version(), newPkg.Version()})
}
@ -305,15 +338,24 @@ func upgradePkgs(aurUp, repoUp upSlice) (stringSet, stringSet, error) {
ignore := make(stringSet)
aurNames := make(stringSet)
if len(aurUp)+len(repoUp) == 0 {
allUpLen := len(repoUp) + len(aurUp)
if allUpLen == 0 {
return ignore, aurNames, nil
}
if !config.UpgradeMenu {
for _, pkg := range aurUp {
aurNames.set(pkg.Name)
}
return ignore, aurNames, nil
}
sort.Sort(repoUp)
sort.Sort(aurUp)
fmt.Printf("%s"+bold(" %d ")+"%s\n", bold(cyan("::")), len(aurUp)+len(repoUp), bold("Packages to upgrade."))
repoUp.Print(len(aurUp) + 1)
aurUp.Print(1)
allUp := append(repoUp, aurUp...)
fmt.Printf("%s"+bold(" %d ")+"%s\n", bold(cyan("::")), allUpLen, bold("Packages to upgrade."))
allUp.print()
fmt.Println(bold(green(arrow + " Packages to not upgrade: (eg: 1 2 3, 1-3, ^4 or repo name)")))
fmt.Print(bold(green(arrow + " ")))

View File

@ -1,12 +1,14 @@
package main
import (
"io/ioutil"
"path/filepath"
"strings"
"fmt"
"unicode"
)
const gitEmptyTree = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
type mapStringSet map[string]stringSet
type intRange struct {
min int
max int
@ -49,39 +51,12 @@ func max(a, b int) int {
return a
}
func addMapStringSet(h map[string]stringSet, n string, v string) {
_, ok := h[n]
func (mss mapStringSet) Add(n string, v string) {
_, ok := mss[n]
if !ok {
h[n] = make(stringSet)
mss[n] = make(stringSet)
}
h[n].set(v)
}
func addMapStringSlice(h map[string][]string, n string, v string) {
_, ok := h[n]
if !ok {
h[n] = make([]string, 0, 1)
}
h[n] = append(h[n], v)
}
func completeFileName(dir, name string) (string, error) {
files, err := ioutil.ReadDir(dir)
if err != nil {
return "", err
}
for _, file := range files {
if file.IsDir() {
continue
}
if strings.HasPrefix(file.Name(), name) {
return filepath.Join(dir, file.Name()), nil
}
}
return "", nil
mss[n].set(v)
}
func lessRunes(iRunes, jRunes []rune) bool {
@ -109,3 +84,47 @@ func lessRunes(iRunes, jRunes []rune) bool {
return len(iRunes) < len(jRunes)
}
func stringSliceEqual(a, b []string) bool {
if a == nil && b == nil {
return true
}
if a == nil || b == nil {
return false
}
if len(a) != len(b) {
return false
}
for i := 0; i < len(a); i++ {
if a[i] != b[i] {
return false
}
}
return true
}
func removeInvalidTargets(targets []string) []string {
filteredTargets := make([]string, 0)
for _, target := range targets {
db, _ := splitDbFromName(target)
if db == "aur" && mode == ModeRepo {
fmt.Printf("%s %s %s\n", bold(yellow(arrow)), cyan(target), bold("Can't use target with option --repo -- skipping"))
continue
}
if db != "aur" && db != "" && mode == ModeAUR {
fmt.Printf("%s %s %s\n", bold(yellow(arrow)), cyan(target), bold("Can't use target with option --aur -- skipping"))
continue
}
filteredTargets = append(filteredTargets, target)
}
return filteredTargets
}

12
vcs.go
View File

@ -18,7 +18,7 @@ type vcsInfo map[string]shaInfos
type shaInfos map[string]shaInfo
type shaInfo struct {
Protocols []string `json:"protocols"`
Brach string `json:"branch"`
Branch string `json:"branch"`
SHA string `json:"sha"`
}
@ -43,7 +43,8 @@ func createDevelDB() error {
bases := getBases(infoMap)
downloadPkgBuilds(info, sliceToStringSet(remoteNames), bases)
toSkip := pkgBuildsToSkip(info, sliceToStringSet(remoteNames))
downloadPkgBuilds(info, bases, toSkip)
tryParsesrcinfosFile(info, srcinfosStale, bases)
for _, pkg := range info {
@ -136,6 +137,7 @@ func getCommit(url string, branch string, protocols []string) string {
cmd := exec.Command(config.GitBin, "ls-remote", protocol+"://"+url, branch)
cmd.Stdout = &outbuf
cmd.Env = append(cmd.Env, "GIT_TERMINAL_PROMPT=0")
err := cmd.Start()
if err != nil {
@ -179,7 +181,7 @@ func (infos shaInfos) needsUpdate() bool {
hasUpdate := make(chan struct{})
checkHash := func(url string, info shaInfo) {
hash := getCommit(url, info.Brach, info.Protocols)
hash := getCommit(url, info.Branch, info.Protocols)
if hash != "" && hash != info.SHA {
hasUpdate <- struct{}{}
} else {
@ -205,10 +207,6 @@ func (infos shaInfos) needsUpdate() bool {
}
}
func inStore(pkgName string) shaInfos {
return savedInfo[pkgName]
}
func saveVCSInfo() error {
marshalledinfo, err := json.MarshalIndent(savedInfo, "", "\t")
if err != nil || string(marshalledinfo) == "null" {

View File

@ -114,18 +114,14 @@ func (rdr *confReader) ParseLine() (tok iniToken, err error) {
rdr.Lineno++
line = bytes.TrimSpace(line)
comment := bytes.IndexByte(line, '#')
if comment >= 0 {
line = line[:comment]
}
if len(line) == 0 {
tok.Type = tokenComment
return
}
switch line[0] {
case '#':
tok.Type = tokenComment
return
case '[':
closing := bytes.IndexByte(line, ']')
if closing < 0 {
@ -205,6 +201,7 @@ lineloop:
curRepo.Servers = append(curRepo.Servers, line.Values...)
continue lineloop
case "Include":
conf.Include = append(conf.Include, line.Values[0])
f, err := os.Open(line.Values[0])
if err != nil {
err = fmt.Errorf("error while processing Include directive at line %d: %s",

View File

@ -89,7 +89,7 @@ func (h Handle) RegisterSyncDb(dbname string, siglevel SigLevel) (*Db, error) {
cName := C.CString(dbname)
defer C.free(unsafe.Pointer(cName))
db := C.alpm_register_syncdb(h.ptr, cName, C.alpm_siglevel_t(siglevel))
db := C.alpm_register_syncdb(h.ptr, cName, C.int(siglevel))
if db == nil {
return nil, h.LastError()
}

View File

@ -62,7 +62,7 @@ func (mod DepMod) String() string {
}
// Signature checking level.
type SigLevel uint
type SigLevel int
const (
SigPackage SigLevel = 1 << iota
@ -76,10 +76,10 @@ const (
SigDatabaseMarginalOk
SigDatabaseUnknownOk
)
const SigUseDefault SigLevel = 1 << 31
const SigUseDefault SigLevel = 1 << 30
// Signature status
type SigStatus uint
type SigStatus int
const (
SigStatusValid SigStatus = iota

View File

@ -534,7 +534,7 @@ func (h Handle) GetDefaultSigLevel() (SigLevel, error) {
}
func (h Handle) SetDefaultSigLevel(siglevel SigLevel) error {
ok := C.alpm_option_set_default_siglevel(h.ptr, C.alpm_siglevel_t(siglevel))
ok := C.alpm_option_set_default_siglevel(h.ptr, C.int(siglevel))
if ok < 0 {
return h.LastError()
@ -552,7 +552,7 @@ func (h Handle) GetLocalFileSigLevel() (SigLevel, error) {
}
func (h Handle) SetLocalFileSigLevel(siglevel SigLevel) error {
ok := C.alpm_option_set_local_file_siglevel(h.ptr, C.alpm_siglevel_t(siglevel))
ok := C.alpm_option_set_local_file_siglevel(h.ptr, C.int(siglevel))
if ok < 0 {
return h.LastError()
@ -570,7 +570,7 @@ func (h Handle) GetRemoteFileSigLevel() (SigLevel, error) {
}
func (h Handle) SetRemoteFileSigLevel(siglevel SigLevel) error {
ok := C.alpm_option_set_remote_file_siglevel(h.ptr, C.alpm_siglevel_t(siglevel))
ok := C.alpm_option_set_remote_file_siglevel(h.ptr, C.int(siglevel))
if ok < 0 {
return h.LastError()

View File

@ -159,18 +159,16 @@ func (pkg Package) OptionalDepends() DependList {
}
// Depends returns the package's check dependency list.
//Exists in futre alpm
/*func (pkg Package) CheckDepends() DependList {
func (pkg Package) CheckDepends() DependList {
ptr := unsafe.Pointer(C.alpm_pkg_get_checkdepends(pkg.pmpkg))
return DependList{(*list)(ptr)}
}*/
}
// Depends returns the package's make dependency list.
//Exists in futre alpm
/*func (pkg Package) MakeDepends() DependList {
func (pkg Package) MakeDepends() DependList {
ptr := unsafe.Pointer(C.alpm_pkg_get_makedepends(pkg.pmpkg))
return DependList{(*list)(ptr)}
}*/
}
// Description returns the package's description.
func (pkg Package) Description() string {

View File

@ -7,7 +7,8 @@ import (
"net/url"
)
const aurURL = "https://aur.archlinux.org/rpc.php?"
//AURURL is the base string from which the query is built
var AURURL = "https://aur.archlinux.org/rpc.php?"
type response struct {
Error string `json:"error"`
@ -46,7 +47,7 @@ type Pkg struct {
func get(values url.Values) ([]Pkg, error) {
values.Set("v", "5")
resp, err := http.Get(aurURL + values.Encode())
resp, err := http.Get(AURURL + values.Encode())
if err != nil {
return nil, err
}

View File

@ -211,6 +211,8 @@ func lexEnv(l *lexer) stateFn {
}
case r == '\t':
l.ignore()
case r == ' ':
l.ignore()
case r == '#':
return lexComment
default:

View File

@ -616,7 +616,7 @@ func isLowerAlpha(c rune) bool {
// check if c is a valid pkgname char
func isValidPkgnameChar(c rune) bool {
return isLowerAlpha(c) || isDigit(c) || c == '@' || c == '.' || c == '_' || c == '+' || c == '-'
return isAlphaNumeric(c) || c == '@' || c == '.' || c == '_' || c == '+' || c == '-'
}
// check if c is a valid pkgver char

View File

@ -74,33 +74,18 @@ func NewCompleteVersion(s string) (*CompleteVersion, error) {
return nil, fmt.Errorf("invalid version format: %s", s)
}
// Older returns true if a is older than the argument version string
func (a *CompleteVersion) Older(v string) bool {
b, err := NewCompleteVersion(v)
if err != nil {
return false
}
// Older returns true if a is older than the argument version
func (a *CompleteVersion) Older(b *CompleteVersion) bool {
return a.cmp(b) == -1
}
// Newer returns true if a is newer than the argument version string
func (a *CompleteVersion) Newer(v string) bool {
b, err := NewCompleteVersion(v)
if err != nil {
return false
}
// Newer returns true if a is newer than the argument version
func (a *CompleteVersion) Newer(b *CompleteVersion) bool {
return a.cmp(b) == 1
}
// Equal returns true if a is equal to the argument version string
func (a *CompleteVersion) Equal(v string) bool {
b, err := NewCompleteVersion(v)
if err != nil {
return false
}
// Equal returns true if a is equal to the argument version
func (a *CompleteVersion) Equal(b *CompleteVersion) bool {
return a.cmp(b) == 0
}