Compare commits

...

799 Commits

Author SHA1 Message Date
dependabot[bot]
6f3bc5cc4c
chore(deps): bump golang.org/x/net from 0.40.0 to 0.41.0 in the go-all group (#2632)
chore(deps): bump golang.org/x/net in the go-all group

Bumps the go-all group with 1 update: [golang.org/x/net](https://github.com/golang/net).


Updates `golang.org/x/net` from 0.40.0 to 0.41.0
- [Commits](https://github.com/golang/net/compare/v0.40.0...v0.41.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.41.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-all
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-11 15:38:14 +02:00
dependabot[bot]
c2caf9c7d7
chore(deps): bump github.com/leonelquinteros/gotext from 1.7.1 to 1.7.2 in the go-all group (#2629)
chore(deps): bump github.com/leonelquinteros/gotext in the go-all group

Bumps the go-all group with 1 update: [github.com/leonelquinteros/gotext](https://github.com/leonelquinteros/gotext).


Updates `github.com/leonelquinteros/gotext` from 1.7.1 to 1.7.2
- [Release notes](https://github.com/leonelquinteros/gotext/releases)
- [Commits](https://github.com/leonelquinteros/gotext/compare/v1.7.1...v1.7.2)

---
updated-dependencies:
- dependency-name: github.com/leonelquinteros/gotext
  dependency-version: 1.7.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: go-all
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-04 23:45:08 +02:00
transifex-integration[bot]
8615239329
Updates for file po/en.po in zh_CN (#2628)
Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-05-28 14:07:22 +02:00
transifex-integration[bot]
c9a8507654
Updates for file po/en.po in ko (#2626)
* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-05-27 14:59:55 +02:00
dependabot[bot]
75e90c3a6d
chore(deps): bump the go-all group with 2 updates (#2622)
Bumps the go-all group with 2 updates: [golang.org/x/net](https://github.com/golang/net) and [golang.org/x/term](https://github.com/golang/term).


Updates `golang.org/x/net` from 0.39.0 to 0.40.0
- [Commits](https://github.com/golang/net/compare/v0.39.0...v0.40.0)

Updates `golang.org/x/term` from 0.31.0 to 0.32.0
- [Commits](https://github.com/golang/term/compare/v0.31.0...v0.32.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.40.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-all
- dependency-name: golang.org/x/term
  dependency-version: 0.32.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-all
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-27 12:15:33 +02:00
Jo
8ab3652846
Fix locale initialization logic (#2619)
* Fix locale initialization logic

* add comment

* Update main.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update GitHub Actions workflow for testing

* Update GitHub Actions workflow to allow artifact overwrite

* Update GitHub Actions workflow to allow artifact overwrite

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-06 18:16:47 +02:00
dependabot[bot]
98be3fec97
chore(deps): bump golang.org/x/sys from 0.32.0 to 0.33.0 in the go-all group (#2620)
chore(deps): bump golang.org/x/sys in the go-all group

Bumps the go-all group with 1 update: [golang.org/x/sys](https://github.com/golang/sys).


Updates `golang.org/x/sys` from 0.32.0 to 0.33.0
- [Commits](https://github.com/golang/sys/compare/v0.32.0...v0.33.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-version: 0.33.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-all
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-06 18:08:17 +02:00
Jo
c6a2226ce1
add locale tooling (#2618) 2025-05-01 22:07:39 +00:00
transifex-integration[bot]
3e82496057
Updates for file po/en.po in hu (#2617)
* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-05-01 23:08:02 +02:00
Jo
a26ac1ba95
Add SECURITY.md with contact and supported version policy (#2616)
* docs: add SECURITY.md with contact and supported version policy

* fix image render
2025-05-01 08:18:31 +00:00
Jo
b745f87210
chore(ci): update golangci lint v2.1.5 (#2615)
* chore(ci): update golangci-lint to v2.1.5 in ci.Dockerfile

* add golangci

* fix lint

* fix lint pkg/upgrade

* reenable lint
2025-05-01 10:00:10 +02:00
dependabot[bot]
b4a41700ee
chore(deps): bump golang.org/x/net from 0.38.0 to 0.39.0 (#2609)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.38.0 to 0.39.0.
- [Commits](https://github.com/golang/net/compare/v0.38.0...v0.39.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.39.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-01 09:58:03 +02:00
dependabot[bot]
0aa80e521e
chore(deps): bump github.com/stretchr/testify from 1.9.0 to 1.10.0 (#2612)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.9.0 to 1.10.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.9.0...v1.10.0)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-version: 1.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-01 09:55:03 +02:00
Jo
bce9f2fc72
Update dependabot.yml (#2614) 2025-05-01 09:54:47 +02:00
dependabot[bot]
5a71db2526
chore(deps): bump github.com/deckarep/golang-set/v2 from 2.7.0 to 2.8.0 (#2611)
Bumps [github.com/deckarep/golang-set/v2](https://github.com/deckarep/golang-set) from 2.7.0 to 2.8.0.
- [Release notes](https://github.com/deckarep/golang-set/releases)
- [Commits](https://github.com/deckarep/golang-set/compare/v2.7.0...v2.8.0)

---
updated-dependencies:
- dependency-name: github.com/deckarep/golang-set/v2
  dependency-version: 2.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-01 08:40:49 +02:00
dependabot[bot]
535370bca0
chore(deps): bump golang.org/x/term from 0.30.0 to 0.31.0 (#2613)
Bumps [golang.org/x/term](https://github.com/golang/term) from 0.30.0 to 0.31.0.
- [Commits](https://github.com/golang/term/compare/v0.30.0...v0.31.0)

---
updated-dependencies:
- dependency-name: golang.org/x/term
  dependency-version: 0.31.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-01 08:37:38 +02:00
Jo
9a3be07887
Create dependabot.yml (#2608)
* Create dependabot.yml

* Update dependabot.yml

* Update dependabot.yml
2025-04-30 21:49:38 +00:00
Jo
2f205ee96c
chore(ci): update golangci-lint to v2.1.5 in ci.Dockerfile (#2607) 2025-04-30 21:45:29 +00:00
Jo
1982ce0366
chore(devcontainer): add devcontainer for clean builds (#2606)
chore(devcontainer): set up passwordless sudo for docker user for clean development and testing
2025-04-30 23:08:02 +02:00
Jo
2dcf94544c
refactor(build): optimize allocations and add tests (#2601)
* perf(build): optimize map/slice allocations and use strings.Builder

* test(build): add tests for parsePackageList
2025-04-29 22:45:24 +02:00
Ferdinand Bachmann
95fc0938fd
fix(installer): Fix the same pkgbase being built multiple times on error (#2561)
fix(installer): Fixes the same pkgbase being built multiple times on error

The previous fix commit ec837c8 failed to address the case where the
build fails, as packages are only added to builtPkgDests on a successful
build. This commit addresses this by adding the package to the map earlier.

Fixes #2560.
2025-04-29 22:45:10 +02:00
transifex-integration[bot]
ff176c0dd2
Updates for file po/en.po in hu (#2605)
* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-04-28 10:17:56 +02:00
Jo
bf315041b1
refactor(upgrade): optimize code and improve maintainability (#2600)
* refactor(upgrade): reduce code duplication in Print methods and fix typo in Filter documentation

* refactor(upgrade): optimize UserExcludeUpgrades with early return and simplify GraphUpgrades function
2025-04-24 12:06:27 +02:00
dependabot[bot]
559bc06b31
chore(deps): bump golang.org/x/net from 0.37.0 to 0.38.0 (#2598)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.37.0 to 0.38.0.
- [Commits](https://github.com/golang/net/compare/v0.37.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-17 11:41:44 +02:00
transifex-integration[bot]
35019f95b6
Updates for file po/en.po in nl (#2592)
* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-20 15:11:04 +01:00
transifex-integration[bot]
50cbf70bf4
Updates for file po/en.po in zh_TW (#2591)
* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-20 15:10:57 +01:00
Jo
0b5f5f0ccd
fix(main): return 1 exit code on panic (#2590) 2025-03-09 22:59:50 +01:00
Jo
a300330b94
fix(ci): prefer ghcr image (#2589)
prefer ghcr image
2025-03-09 22:50:58 +01:00
Jo
670598912e
fix: correct Docker manifest creation in builder image workflow (#2588)
* fix: correct Docker manifest creation in builder image workflow

- Fix "invalid reference format" error by properly separating Docker Hub and
  GitHub Container Registry tags
- Use short SHA format instead of long format for more manageable tags
- Improve manifest list creation process to handle multiple registries correctly
- Ensure proper handling of ghcr.io prefix for GitHub Container Registry

* update golangci and comment out community
2025-03-09 22:20:50 +01:00
Jo
257b230e39
chore(yay): update deps (#2587)
update deps
2025-03-09 20:45:10 +01:00
Jo
d2c67ae0a4
fix(yay): fix minor performance and safety issue (#2581) 2025-02-25 17:15:34 +01:00
Jo
4432c60246 fix(ci): fix issue with builder image CI 2025-02-20 17:16:35 +00:00
Jo
33ba07fe0d
fix(yay): update go mod (#2580)
update go mod
2025-02-17 15:54:21 +01:00
Dominik Link
d37e365ac3
fixed issue #2471 where it wouldn't show info when packages are in a group (#2576)
fixed issue #2471 where it wouldn't show info when packages are in a group and modified test
2025-02-17 15:28:35 +01:00
transifex-integration[bot]
6807ecc081
Updates for file po/en.po in zh_TW (#2571)
* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

* Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-02-14 14:36:32 +01:00
transifex-integration[bot]
b07d8c1447
Updates for file po/en.po in de (#2572)
Translate po/en.po in de

100% translated source file: 'po/en.po'
on 'de'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-02-14 14:36:13 +01:00
transifex-integration[bot]
3da808847f
Updates for file po/en.po in ru_RU (#2574)
Translate po/en.po in ru_RU

100% translated source file: 'po/en.po'
on 'ru_RU'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-02-05 12:01:03 +01:00
transifex-integration[bot]
590a3d3a8c
Updates for file po/en.po in ru (#2575)
Translate po/en.po in ru

100% translated source file: 'po/en.po'
on 'ru'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-02-05 12:00:33 +01:00
Ikko Eltociear Ashimine
69685d0fb5
docs: update README.md (#2564)
minor fix
2025-01-23 14:42:52 +01:00
transifex-integration[bot]
5572d1817e
Updates for file po/en.po in zh_TW (#2569)
Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-01-15 11:11:08 +01:00
transifex-integration[bot]
2f403a4f28
Updates for file po/en.po in zh_CN (#2568)
* Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

* Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

* Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

* Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

* Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-01-15 11:09:49 +01:00
transifex-integration[bot]
9322197d0c
Updates for file po/en.po in ca_ES (#2556)
Translate po/en.po in ca_ES

100% translated source file: 'po/en.po'
on 'ca_ES'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-01-03 16:06:02 +01:00
transifex-integration[bot]
46f3842e6f
Updates for file po/en.po in ca (#2555)
Translate po/en.po in ca

100% translated source file: 'po/en.po'
on 'ca'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-01-03 14:55:04 +01:00
transifex-integration[bot]
76000ae987
Updates for file po/en.po in fr (#2557)
* Translate po/en.po in fr

100% translated source file: 'po/en.po'
on 'fr'.

* Translate po/en.po in fr

100% translated source file: 'po/en.po'
on 'fr'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-01-03 14:54:45 +01:00
transifex-integration[bot]
e27979d21d
Updates for file po/en.po in fr_FR (#2558)
* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-01-03 14:54:16 +01:00
transifex-integration[bot]
c0baacd633
Updates for file po/en.po in sv (#2559)
Translate po/en.po in sv

100% translated source file: 'po/en.po'
on 'sv'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-12-30 10:57:45 +00:00
transifex-integration[bot]
43567b5d85
Updates for file po/en.po in it_IT (#2548)
Translate po/en.po in it_IT

100% translated source file: 'po/en.po'
on 'it_IT'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-12-23 14:22:27 +00:00
transifex-integration[bot]
e18cc87307
Updates for file po/en.po in hu (#2549)
Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-12-23 14:22:06 +00:00
transifex-integration[bot]
81a2a19101
Updates for file po/en.po in id (#2550)
Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-12-22 15:28:42 +00:00
transifex-integration[bot]
669d7af6d1
Updates for file po/en.po in nl (#2551)
Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-12-22 15:27:33 +00:00
transifex-integration[bot]
65ce4b9f6f
Updates for file po/en.po in pt (#2553)
Translate po/en.po in pt

100% translated source file: 'po/en.po'
on 'pt'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-12-22 15:27:04 +00:00
transifex-integration[bot]
5f2b94ce7c
Updates for file po/en.po in pt_BR (#2552)
* Translate po/en.po in pt_BR

100% translated source file: 'po/en.po'
on 'pt_BR'.

* Translate po/en.po in pt_BR

100% translated source file: 'po/en.po'
on 'pt_BR'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-12-22 15:26:31 +00:00
dmitrodem
3f2f6eae31
socks5 support (#2543)
* socks5 support

socks5 support via environment variable, e.g. SOCKS5_PROXY=localhost:1080 yay ...

* use default transport and update tests to work on arm
2024-12-20 16:49:05 +00:00
scarf
da53d3855f
fix: use positional argument for "providers available" translation (#2537) 2024-12-20 16:43:28 +00:00
Matthias Kurz
f23fe98a66
Fix comments: AUR_SEEN is the correct name (#2535)
Fix comments: AUR_SEEN is the correct name
2024-11-21 12:00:11 +01:00
Ferdinand Bachmann
ec837c831d
fix(installer): Fixes the same pkgbase being built multiple times (#2534)
* fix(installer): Fixes the same pkgbase being built multiple times

When building a PKGBUILD pkgbase with multiple pkgnames,
installAURPackages() invokes buildPkg() multiple times for the same
pkgbase. This causes prepare() to be run multiple times for the same
pkgbase, since detection of already built packages happens after
prepare().

Additionally, detection of already built packages can fail if the split
debug packages are enabled and the package does not contain any
binaries, causing no -debug package to be created by makepkg even though
it is listed by makepkg --packagelist.

This commit fixes this by keeping track of the pkgdests built by
buildPkg() and avoiding rebuilds of the same pkgbase in the same yay
invocation.

Fixes #2340.

Signed-off-by: Ferdinand Bachmann <ferdinand.bachmann@yrlf.at>

* fix(installer): Fixes buildPkg() isTarget param being order-dependent

Previously, the buildPkg invocation for a pkgbase only considered
whether the current pkgname is part of installer.origTargets. This made
the decision whether to rebuild the package order-dependent.

This commit fixes this by keeping track of which pkgbases are part of
installer.origTargets and rebuilding the pkgbase if any of its pkgnames
is part of origTargets.

* fix(tests): Test that installing split packages avoids rebuilds

The previous two commits changed how split packages (packages with the same
pkgbase) are built, ensuring that those packages aren't built multiple
times.

This commit updates the lists of commands that the tests expect to be
run so that `makepkg` isn't run multiple times per pkgbase.

---------

Signed-off-by: Ferdinand Bachmann <ferdinand.bachmann@yrlf.at>
2024-11-19 11:08:28 +01:00
transifex-integration[bot]
f100c1d54b
Updates for file po/en.po in uk (#2533)
Translate po/en.po in uk

100% translated source file: 'po/en.po'
on 'uk'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-11-15 14:52:12 +01:00
April Hall
3f5d26c4f9
Replace "⚠️ {warning}" with Github Warning Blocks (#2531)
Update README.md
2024-11-12 20:03:03 +00:00
Alice Jacka
3003f1667c
Add missing sudo to binary install instructions (#2525) 2024-10-25 11:12:49 +02:00
Anmol W
fb168fb176
fix swapped fish completion descriptions for --repo and --aur (#2523)
fix swapped fish completions for --repo and --aur
2024-10-25 10:35:07 +02:00
transifex-integration[bot]
842067256b
Updates for file po/en.po in id (#2522)
* Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

* Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

* Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

* Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

* Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

* Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

* Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

* Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

* Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

* Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

* Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

* Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

* Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

* Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

* Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-10-25 10:30:32 +02:00
transifex-integration[bot]
a6a6dc0acb
Updates for file po/en.po in hu (#2521)
* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-10-25 10:30:21 +02:00
transifex-integration[bot]
2e06552211
Updates for file po/en.po in zh_CN (#2524)
* Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

* Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

* Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

* Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

* Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

* Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

* Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-10-25 10:30:11 +02:00
transifex-integration[bot]
138c2dd6cd
Updates for file po/en.po in sk (#2516)
Translate po/en.po in sk

100% translated source file: 'po/en.po'
on 'sk'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-09-30 09:32:30 +02:00
transifex-integration[bot]
4872b8b829
Updates for file po/en.po in pl_PL (#2513)
Translate po/en.po in pl_PL

100% translated source file: 'po/en.po'
on 'pl_PL'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-09-27 10:33:58 +02:00
transifex-integration[bot]
1b6ad7b305
Updates for file po/en.po in hu (#2512)
Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-09-27 10:33:51 +02:00
transifex-integration[bot]
d6e961af70
Updates for file po/en.po in pl (#2514)
Translate po/en.po in pl

100% translated source file: 'po/en.po'
on 'pl'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-09-27 10:33:41 +02:00
Jo
89b32ee9ce
fix(parser): allow disable-sandbox option. fixes #2509 (#2511) 2024-09-24 08:12:41 +00:00
transifex-integration[bot]
f68a57129f
Updates for file po/en.po in hu (#2510)
* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-09-24 09:57:49 +02:00
Absobel
bea53a4a09
fix: added missing spaces (#2386) 2024-09-19 14:15:44 +02:00
Rodrigo Barrios
13df7e79eb
Document -Ycc in manpage (#2482)
Signed-off-by: r.b4rr10s <rodrigoedu11@gmail.com>
2024-09-19 14:14:16 +02:00
Tim Konick
0f496c9af9
Fixes syntax error #2506 (#2507)
* Fixes syntax error

* Use escape-chars instead
2024-09-18 07:46:51 +00:00
Jo
84d8f1b7b3
chore: update default po (#2505)
update default po
2024-09-17 16:50:02 +00:00
Jo
3a118b7690
chore: fix failing build actions (#2503) 2024-09-17 17:14:26 +02:00
transifex-integration[bot]
a32f5e7e2c
Updates for file po/en.po in nl (#2504)
* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-09-17 14:01:14 +02:00
Oliver Tzeng
3c881d577f
fix(zh_TW.po): fixed a lot of simplified chinese (#2498)
- Changelog
 * Added myself as the initial translator, got removed for some
   reason(#1776)
 * Converted simplified to traditional Chinese
 * Formatting
2024-09-14 17:52:30 +02:00
transifex-integration[bot]
86f5c08ec4
Updates for file po/en.po in he (#2491)
* Translate po/en.po in he

100% translated source file: 'po/en.po'
on 'he'.

* Translate po/en.po in he

100% translated source file: 'po/en.po'
on 'he'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-09-14 17:20:59 +02:00
Stephan Burns
675f0ba3f3
Add sudo to install instructions so it can be run without user switching (#2483)
* Add sudo to install instructions so it can be run without user switching

* Add sudo to one liner and a note for people using different root tools
2024-09-02 15:21:57 +02:00
transifex-integration[bot]
d7d67e3fd3
Updates for file po/en.po in hu (#2490)
* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-09-02 15:21:04 +02:00
transifex-integration[bot]
c28be1d8b0
Updates for file po/en.po in ru_RU (#2481)
* Translate po/en.po in ru_RU

100% translated source file: 'po/en.po'
on 'ru_RU'.

* Translate po/en.po in ru_RU

100% translated source file: 'po/en.po'
on 'ru_RU'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-08-07 09:56:31 +02:00
iTrooz
1b8f823f7c
Refactor part of UserExcludeUpgrades() (#2450) 2024-07-29 09:15:20 +02:00
Daniel Oh
836fc5922a
add short option for --repo (#2380)
* add short option for --repo

* run pre-commit

* update man page

* add fish completion

* add a N options

* add long options

---------

Co-authored-by: jguer <me@jguer.space>
2024-06-28 16:40:35 +02:00
Joey Holtzman
0165486bf4
Respect provided targets when using -Si flag (#2460) 2024-06-28 16:39:49 +02:00
Jo
5149e3714d
fix(query): match empty pacman -Si with AUR info (#2459) 2024-06-21 10:06:02 +02:00
Marcus B Spencer
9ed9b0b4e1
Update README.md (#2458)
Update the README to prevent partial upgrades.

Partial upgrades are dangerous on Arch Linux.
Partial upgrades may occur when a `-y` operation is given without a corresponding `-u` operation.

Quote from the ArchWiki:
> Do not use:
> `pacman -Sy package`
> `pacman -Sy` followed by `pacman -S` package (Note the absence of `-S**u**` in the installation of the package.)
> `pacman -Syuw` (Note that `pacman -Syuw` does imply the same risks like pacman -Sy`, as it will update the pacman sync database without installing the newer packages.)

https://wiki.archlinux.org/title/System_maintenance#Partial_upgrades_are_unsupported
2024-06-20 14:15:14 +02:00
transifex-integration[bot]
e19700234f
Updates for file po/en.po in he (#2447)
* Translate po/en.po in he

100% translated source file: 'po/en.po'
on 'he'.

* Translate po/en.po in he

100% translated source file: 'po/en.po'
on 'he'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-03 09:56:18 +02:00
transifex-integration[bot]
965f8956e9
Updates for file po/en.po in he_IL (#2448)
Translate po/en.po in he_IL

100% translated source file: 'po/en.po'
on 'he_IL'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-03 09:38:39 +02:00
transifex-integration[bot]
53c9d0ef34
Updates for file po/en.po in es (#2441)
* Translate po/en.po in es

100% translated source file: 'po/en.po'
on 'es'.

* Translate po/en.po in es

100% translated source file: 'po/en.po'
on 'es'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-13 09:23:23 +02:00
Adivardhan Maheshwari
9b41f136d6
Add a package update command (#2205)
* Add a package update command

when running these commands on archlinux-base proxmox lxc, found out that these commands return 404 error unless the pacman -Sy command is run.

This although seeming simplistic might be something unknown when someone uses yay and archlinux for the first time.

* Update README.md
2024-05-02 11:00:29 +00:00
Tom
d956dd7888
Fix incorrect formatting of some msgstr of the spanish translation (#2437)
fix: Fix incorrect formatting for "%s to upgrade/install." and "%s will also be installed for this operation." in Spanish translation
2024-05-02 12:58:59 +02:00
eveneast
61dd708a4a
chore: fix function name in comment (#2430)
Signed-off-by: eveneast <qcqs@foxmail.com>
2024-05-02 10:54:16 +00:00
transifex-integration[bot]
ff3ad18fa8
Updates for file po/en.po in fr (#2425)
* Translate po/en.po in fr

100% translated source file: 'po/en.po'
on 'fr'.

* Translate po/en.po in fr

100% translated source file: 'po/en.po'
on 'fr'.

* Translate po/en.po in fr

100% translated source file: 'po/en.po'
on 'fr'.

* Translate po/en.po in fr

100% translated source file: 'po/en.po'
on 'fr'.

* Translate po/en.po in fr

100% translated source file: 'po/en.po'
on 'fr'.

* Translate po/en.po in fr

100% translated source file: 'po/en.po'
on 'fr'.

* Translate po/en.po in fr

100% translated source file: 'po/en.po'
on 'fr'.

* Translate po/en.po in fr

100% translated source file: 'po/en.po'
on 'fr'.

* Translate po/en.po in fr

100% translated source file: 'po/en.po'
on 'fr'.

* Translate po/en.po in fr

100% translated source file: 'po/en.po'
on 'fr'.

* Translate po/en.po in fr

100% translated source file: 'po/en.po'
on 'fr'.

* Translate po/en.po in fr

100% translated source file: 'po/en.po'
on 'fr'.

* Translate po/en.po in fr

100% translated source file: 'po/en.po'
on 'fr'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-02 12:53:37 +02:00
transifex-integration[bot]
803f708106
Updates for file po/en.po in fr_FR (#2424)
* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-02 12:53:29 +02:00
transifex-integration[bot]
9b6d40d7f9
Updates for file po/en.po in hu (#2431)
* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-02 12:53:20 +02:00
transifex-integration[bot]
779a9f16bd
Updates for file po/en.po in fr_FR (#2423)
* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-04-03 14:32:00 +02:00
transifex-integration[bot]
02d3e2e1c0
Updates for file po/en.po in ru_RU (#2422)
Translate po/en.po in ru_RU

100% translated source file: 'po/en.po'
on 'ru_RU'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-04-03 14:31:51 +02:00
transifex-integration[bot]
8de397ed11
Updates for file po/en.po in fr (#2419)
Translate po/en.po in fr

100% translated source file: 'po/en.po'
on 'fr'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-04-03 10:37:48 +02:00
transifex-integration[bot]
de7ad4070f
Updates for file po/en.po in zh_TW (#2420)
Translate po/en.po in zh_TW

100% translated source file: 'po/en.po'
on 'zh_TW'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-04-03 10:37:41 +02:00
transifex-integration[bot]
3b18e2197c
Updates for file po/en.po in ca_ES (#2421)
Translate po/en.po in ca_ES

100% translated source file: 'po/en.po'
on 'ca_ES'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-04-03 10:37:33 +02:00
transifex-integration[bot]
127b3a5b1a
Updates for file po/en.po in fr_FR (#2414)
* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

* Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-03-31 16:53:36 +02:00
Jo
2ff794da32
chore: update go-alpm (#2409)
* update go alpm

* update golangci

* add missing ldflag
2024-03-23 23:46:02 +01:00
James Raspass
6c2330528f
Use Go 1.21's min/max built-ins (#2405)
This simplifies the code compared to either rolling our own or awkwardly
using math's float functions with integers.
2024-03-23 23:27:30 +01:00
transifex-integration[bot]
a1d530cbf4
Updates for file po/en.po in hu (#2401)
* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-03-17 14:14:46 +01:00
James Raspass
5d887cbd41
Replace github.com/pkg/errors with stdlib errors/fmt (#2400) 2024-03-16 09:10:49 +01:00
Jo
05b76852bd
chore(yay): fix missing command (#2399)
fix missing command
2024-03-16 01:22:48 +01:00
Jo
9524cbbaed
chore: ensure pacman update in builder (#2398)
* ensure pacman up

* lint step fix
2024-03-16 01:13:30 +01:00
Jo
48d1d3d2d5
chore(yay): update dependencies and builder (#2396)
* fix libalpm14 build

* update deps

* try alternative arch
2024-03-16 00:55:05 +01:00
transifex-integration[bot]
741d83c1f0
Updates for file po/en.po in fr_FR (#2388)
Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-03-15 23:30:13 +01:00
transifex-integration[bot]
9c02af429a
Updates for file po/en.po in ru (#2389)
* Translate po/en.po in ru

100% translated source file: 'po/en.po'
on 'ru'.

* Translate po/en.po in ru

100% translated source file: 'po/en.po'
on 'ru'.

* Translate po/en.po in ru

100% translated source file: 'po/en.po'
on 'ru'.

* Translate po/en.po in ru

100% translated source file: 'po/en.po'
on 'ru'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-03-11 15:27:27 +01:00
Jo
03e89d660f
chore: fix min go version (#2383)
fix min go version
2024-03-02 16:40:21 +01:00
transifex-integration[bot]
f7f2169992
Updates for file po/en.po in nl (#2377)
* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

* Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-02-23 08:28:24 +01:00
Jo
e8080f87c2
chore(yay): fix breaking -git ci test (#2373)
* chore(yay): fix breaking test

* chore(yay): fix breaking test

* chore(yay): fix breaking test

* update gomod

* remove debug commands
2024-02-19 17:04:06 +01:00
Jo
26aa171b2b
fix(query): remove -debug packages from missing list if base package is installed (#2372)
* chore(yay): fix pre-commit

* chore(yay): fix git ignore
2024-02-19 10:29:47 +00:00
transifex-integration[bot]
92d7cb0faa
Updates for file po/en.po in id (#2366)
Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-02-02 09:41:56 +01:00
transifex-integration[bot]
f3a4fc8987
Updates for file po/en.po in cs (#2367)
Translate po/en.po in cs

100% translated source file: 'po/en.po'
on 'cs'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-02-02 09:41:48 +01:00
transifex-integration[bot]
aa6cad75a3
Updates for file po/en.po in ca (#2353)
Translate po/en.po in ca

100% translated source file: 'po/en.po'
on 'ca'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-01-25 16:44:40 +01:00
transifex-integration[bot]
2078bc936f
Updates for file po/en.po in es (#2354)
Translate po/en.po in es

100% translated source file: 'po/en.po'
on 'es'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-01-25 16:44:23 +01:00
transifex-integration[bot]
d778be4f9f
Updates for file po/en.po in de (#2352)
Translate po/en.po in de

100% translated source file: 'po/en.po'
on 'de'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-01-25 16:44:06 +01:00
transifex-integration[bot]
aeafe23027
Updates for file po/en.po in hu (#2355)
Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-01-25 16:43:51 +01:00
transifex-integration[bot]
6c31477ccd
Updates for file po/en.po in pt_BR (#2357)
Translate po/en.po in pt_BR

100% translated source file: 'po/en.po'
on 'pt_BR'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-01-25 16:43:39 +01:00
transifex-integration[bot]
0a930c9ffc
Updates for file po/en.po in id (#2358)
Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-01-25 16:43:15 +01:00
transifex-integration[bot]
d411524481
Updates for file po/en.po in pt (#2356)
Translate po/en.po in pt

100% translated source file: 'po/en.po'
on 'pt'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-01-25 16:42:51 +01:00
transifex-integration[bot]
15ef062bb5
Updates for file po/en.po in zh_CN (#2359)
Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-01-25 16:42:28 +01:00
transifex-integration[bot]
c2d7d99e43
Updates for file po/en.po in it_IT (#2360)
Translate po/en.po in it_IT

100% translated source file: 'po/en.po'
on 'it_IT'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-01-25 16:42:20 +01:00
transifex-integration[bot]
86207fce64
Updates for file po/en.po in sv (#2361)
Translate po/en.po in sv

100% translated source file: 'po/en.po'
on 'sv'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-01-25 16:42:00 +01:00
Jo
dc68b1a8fa
chore: update locale files (#2351)
update po
2024-01-25 15:16:21 +00:00
Joey Holtzman
d02c45e5b6
Remove deprecated flags in favor of boolean flags (#2350) 2024-01-25 16:03:47 +01:00
Joey Holtzman
8d773aa6a3
Remove deprecated warning when using -yc together (#2347) 2024-01-24 16:11:51 +01:00
transifex-integration[bot]
b81d34d5cc
Updates for file po/en.po in zh_CN (#2341)
Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-01-18 15:10:57 +01:00
transifex-integration[bot]
d77dd77141
Updates for file po/en.po in hu (#2338)
* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-01-04 17:51:26 +01:00
transifex-integration[bot]
e34bce003d
Updates for file po/en.po in hu (#2337)
* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

* Translate po/en.po in hu

100% translated source file: 'po/en.po'
on 'hu'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-12-27 16:59:38 +01:00
Eng Zer Jun
d23e8925fa
fix(dep_graph): fix panic on selecting AUR providers (#2333)
This commit fixes https://github.com/Jguer/yay/issues/2289 by making
`provideMenu` returns the first option when it receives an error input
from the user (e.g. user sends EOF).

Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2023-12-11 17:45:54 +01:00
Jo
643830fccd
chore: Use custom image (#2328)
* self-host image

* update deps
2023-11-30 09:39:56 +01:00
transifex-integration[bot]
cb4cd7b451
Updates for file po/en.po in sv (#2321)
Translate po/en.po in sv

100% translated source file: 'po/en.po'
on 'sv'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-11-29 11:17:30 +01:00
transifex-integration[bot]
350ff1c70a
Updates for file po/en.po in id (#2323)
Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-11-29 11:17:16 +01:00
transifex-integration[bot]
26dc74ed67
Updates for file po/en.po in ca (#2320)
Translate po/en.po in ca

100% translated source file: 'po/en.po'
on 'ca'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-11-29 11:16:57 +01:00
transifex-integration[bot]
a00ff6b3cc
Updates for file po/en.po in de (#2324)
Translate po/en.po in de

100% translated source file: 'po/en.po'
on 'de'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-11-29 11:16:43 +01:00
transifex-integration[bot]
9e665a98b9
Updates for file po/en.po in pt_BR (#2325)
* Translate po/en.po in pt_BR

100% translated source file: 'po/en.po'
on 'pt_BR'.

* Translate po/en.po in pt_BR

100% translated source file: 'po/en.po'
on 'pt_BR'.

* Translate po/en.po in pt_BR

100% translated source file: 'po/en.po'
on 'pt_BR'.

* Translate po/en.po in pt_BR

100% translated source file: 'po/en.po'
on 'pt_BR'.

* Translate po/en.po in pt_BR

100% translated source file: 'po/en.po'
on 'pt_BR'.

* Translate po/en.po in pt_BR

100% translated source file: 'po/en.po'
on 'pt_BR'.

* Translate po/en.po in pt_BR

100% translated source file: 'po/en.po'
on 'pt_BR'.

* Translate po/en.po in pt_BR

100% translated source file: 'po/en.po'
on 'pt_BR'.

* Translate po/en.po in pt_BR

100% translated source file: 'po/en.po'
on 'pt_BR'.

* Translate po/en.po in pt_BR

100% translated source file: 'po/en.po'
on 'pt_BR'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-11-29 11:16:30 +01:00
transifex-integration[bot]
4169f0ee42
Updates for file po/en.po in es (#2326)
* Translate po/en.po in es

100% translated source file: 'po/en.po'
on 'es'.

* Translate po/en.po in es

100% translated source file: 'po/en.po'
on 'es'.

* Translate po/en.po in es

100% translated source file: 'po/en.po'
on 'es'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-11-29 11:16:15 +01:00
transifex-integration[bot]
5a6b18fe23
Updates for file po/en.po in pt (#2322)
* Translate po/en.po in pt

100% translated source file: 'po/en.po'
on 'pt'.

* Translate po/en.po in pt

100% translated source file: 'po/en.po'
on 'pt'.

* Translate po/en.po in pt

100% translated source file: 'po/en.po'
on 'pt'.

* Translate po/en.po in pt

100% translated source file: 'po/en.po'
on 'pt'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-11-23 16:34:20 +01:00
transifex-integration[bot]
c8577bb613
Updates for file po/en.po in pt_BR (#2317)
Translate po/en.po in pt_BR

100% translated source file: 'po/en.po'
on 'pt_BR'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-11-21 14:10:18 +01:00
transifex-integration[bot]
965f41b938
Updates for file po/en.po in it_IT (#2316)
Translate po/en.po in it_IT

100% translated source file: 'po/en.po'
on 'it_IT'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-11-21 14:10:06 +01:00
Kirill Motkov
0771ded99b
Refactor AURPKGBUILDRepos and Fix Localization Command in Makefile (#2313)
* optimize mutex usage and logging in AURPKGBUILDRepos function

* fix localization script

Fix localization script Makefile by adding missing semicolon and --no-translator flag
2023-11-20 16:58:20 +01:00
transifex-integration[bot]
8f98ab3d4b
Updates for file po/en.po in de (#2312)
Translate po/en.po in de

100% translated source file: 'po/en.po'
on 'de'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-11-15 16:43:40 +01:00
Joey Holtzman
d368f99be0
feat(yay): add boolean flags (#2285)
* feat(yay): add boolean flags

This feature now allows users to specify --<option>=<bool value> instead
of using --<option> and --no<option>. Specifying nothing results in the
boolean value being true. The flags prefixed with `no` are deprecated.

* chore(args): Print warning when using deprecated flags

* chore(yay): Update man page for deprecated options and add examples of
boolean flags being used
2023-09-27 12:36:57 +02:00
Eng Zer Jun
a1121556be
refactor: remove redundant len check (#2291)
`len` returns 0 if the slice or map is nil. From the Go specification
[1]:

  "1. For a nil slice, the number of iterations is 0."
  "3. If the map is nil, the number of iterations is 0."

Therefore, an additional `len(v) != 0` check for before the loop is
unnecessary.

[1]: https://go.dev/ref/spec#For_range

Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2023-09-27 12:36:08 +02:00
Jo
299aa1e123
fix(download): do not garble download output by default (#2283)
limit default concurrent downloads to 1
2023-09-18 09:51:32 +02:00
Joey Holtzman
04c76a404e
feat(install): add --keepsrc to keep pkg/ and src/ directories (#2272)
* feat(install): add --nocleanbuild to keep pkg/ and src/ directories for
AUR packages

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

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

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

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

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

---------

Co-authored-by: jguer <me@jguer.space>
2023-09-18 09:21:42 +02:00
Jo
e60ccdf8b7
Fix image recursive build (#2280)
* fix image recursive build

* add test fixes

* disable buildvcs

* fix integration
2023-09-15 11:17:18 +02:00
transifex-integration[bot]
b6c72ce7a2
Updates for file po/en.po in he on branch next (#2274)
Translate po/en.po in he

100% translated source file: 'po/en.po'
on 'he'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-09-08 18:21:41 +02:00
Esteban Blanc
87d1fd1c06
Add oneliner install for binray source (#2268) 2023-09-07 10:58:30 +02:00
transifex-integration[bot]
92d50910de
Updates for file po/en.po in ko on branch next (#2266)
* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-09-07 10:44:11 +02:00
Joey Holtzman
a0e6838a5f
fix(yay): fix missing placeholders in translations (#2271) 2023-09-07 10:43:45 +02:00
Jo
8916cd174b
refactor(yay): move cfg inside of runtime (#2259)
* rework relationship between runtime and cfg

* separate runtime from cfg

* simplify instantiation logic

* move installer to appropriate package

* move operator to sync package

* add tests for srcinfo service

* consolidate srcinfo service in sync

* add logger to srcinfo

* add logger to preparer

* remove unused text functions

* remove remaining text.* from srcinfo

* remove global logger parts

* remove global org method exports

* remove global logger

* move text->input

* add rule to prevent fmt.Print

* update golangci go version

* remove outdated FAQs

* remove outdated FAQs
2023-08-06 21:39:41 +02:00
Jo
7483393377
fix(sync): add missing Replaces to -Si (#2257) 2023-08-02 07:45:14 +00:00
jguer
9aefb8440e
add missing login 2023-07-30 23:16:30 +02:00
Jo
6c1998f6eb
chore(ci): update builder image ci (#2254) 2023-07-30 22:07:28 +02:00
smolx
688434b242
chore(yay): remove unnecessary Graph initialization (#2251)
Just an additional correction to one of my commits.
2023-07-26 10:28:23 +02:00
Jo
5995e55ddb
chore(topo): move topo to where it's used (#2250)
move topo into dep
2023-07-23 20:20:05 +00:00
Jo
04c82b8112
chore(yay): replace custom set package with dep (#2249)
* replace string set with dep

* remove unused field

* remove custom string set package
2023-07-23 17:29:01 +00:00
transifex-integration[bot]
abd398a787
Updates for file po/en.po in nl on branch next (#2243)
Translate po/en.po in nl

100% translated source file: 'po/en.po'
on 'nl'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-07-16 09:08:11 +00:00
Jo
23b053bccf
fix(aur): only check gpg signature after gpg import . fixes #2165 (#2239)
only check gpg on install
2023-07-10 09:54:43 +02:00
Joey Holtzman
dadc8c0d98
Re-add functionality for Installed and NotInstalled options in the menus (#2233)
* fix(menus): Handle Installed and NotInstalled options correctly in the
menus

This functionality was temporarily removed. This commit adds that
functionality back.

* fix(tests): Mock InstalledRemotePackageNamesFn when necessary
2023-07-06 06:54:21 +00:00
smolx
6dd7933fbe
Fix handling targets with specified db (#2218)
* Fix handling targets with specified db

Handle it in a similar way to handling targets with unspecified db.

Also refactored GraphSyncPkg method to make stuff more DRY.

* update go-mod
2023-07-06 06:53:46 +00:00
Joey Holtzman
d9029face3
Don't upgrade repo packages when --aur is specified (#2234)
fix(aur_install): Don't upgrade repo packages when --aur is specified
2023-07-06 06:20:51 +00:00
transifex-integration[bot]
64f5c2b0a9
Updates for po/en.po in zh_CN (#2232)
* Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

* Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

* Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

* Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

* Translate po/en.po in zh_CN

100% translated source file: 'po/en.po'
on 'zh_CN'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-07-06 06:19:41 +00:00
transifex-integration[bot]
93afb03738
Updates for po/en.po in pt (#2231)
* Translate po/en.po in pt

100% translated source file: 'po/en.po'
on 'pt'.

* Translate po/en.po in pt

100% translated source file: 'po/en.po'
on 'pt'.

* Translate po/en.po in pt

100% translated source file: 'po/en.po'
on 'pt'.

* Translate po/en.po in pt

100% translated source file: 'po/en.po'
on 'pt'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-06-26 08:33:32 +02:00
transifex-integration[bot]
0dcf911e99
Updates for po/en.po in it_IT (#2229)
Translate po/en.po in it_IT

100% translated source file: 'po/en.po'
on 'it_IT'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-06-23 20:46:09 +02:00
transifex-integration[bot]
2be57cb312
Updates for po/en.po in ko (#2226)
Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-06-23 20:45:59 +02:00
transifex-integration[bot]
f070cff9f9
Updates for po/en.po in sv (#2227)
Translate po/en.po in sv

100% translated source file: 'po/en.po'
on 'sv'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-06-23 20:45:52 +02:00
transifex-integration[bot]
c46f5d31cc
Updates for po/en.po in ru (#2225)
Translate po/en.po in ru

100% translated source file: 'po/en.po'
on 'ru'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-06-23 20:45:47 +02:00
transifex-integration[bot]
74c1cdb254
Updates for po/en.po in id (#2224)
Translate po/en.po in id

100% translated source file: 'po/en.po'
on 'id'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-06-23 20:45:37 +02:00
transifex-integration[bot]
79b03fdac1
Updates for po/en.po in pt_BR (#2223)
Translate po/en.po in pt_BR

100% translated source file: 'po/en.po'
on 'pt_BR'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-06-23 20:45:30 +02:00
transifex-integration[bot]
5a3c3ae4d0
Updates for po/en.po in fr_FR (#2228)
Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-06-23 20:45:04 +02:00
transifex-integration[bot]
710ff0097a
Updates for po/en.po in es (#2222)
Translate po/en.po in es

100% translated source file: 'po/en.po'
on 'es'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-06-23 20:44:34 +02:00
transifex-integration[bot]
ddeaf47a53
Updates for po/en.po in ca (#2221)
Translate po/en.po in ca

100% translated source file: 'po/en.po'
on 'ca'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-06-23 20:44:23 +02:00
Jo
4f7b3edefe
chore(yay): update deps (#2220)
* update deps

* update po
2023-06-23 20:15:47 +02:00
Jo
5b8cc98afa
fix(dep_graph): do not package provides when providing itself. fix: #2215 (#2216)
fix #2215. package provides itself
2023-06-19 08:13:47 +00:00
transifex-integration[bot]
e25d00015a
Updates for po/en.po in ru (#2212)
Translate po/en.po in ru

100% translated source file: 'po/en.po'
on 'ru'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-06-17 21:02:31 +00:00
smolx
c5a18e5000
Fix install reason preservation when package is reinstalled (#2195) 2023-06-11 16:16:05 +00:00
smolx
adde043514
Add --askyesremovemake option (#2199)
Same as --askremovemake option but with "Y" as a default answer.
2023-06-11 16:13:01 +00:00
smolx
7dc4fae155
Fix excluding of packages with unsatisfied deps (#2203)
* Fix excluding of packages with unsatisfied deps

When dependency is unsatisfied, add to the graph not only a dep node,
but relationship with parent too.

* Remove excess(duplicate) logic

* Add test cases of upgrading with unsatisfied deps
2023-06-11 16:10:48 +00:00
transifex-integration[bot]
599a5a9073
Updates for po/en.po in pt_BR (#2206)
Translate po/en.po in pt_BR

100% translated source file: 'po/en.po'
on 'pt_BR'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-06-11 16:10:06 +00:00
smolx
12282fb28a
Fix sorting by name in package search (#2198)
Slightly reorganize and add more tests for SourceQueryBuilder.
2023-06-03 14:01:17 +00:00
smolx
0607090719
Fix deps resolving for installs from SRCINFO (#2190)
Also made MockBuilder and MockRunner thread-safe.
2023-06-03 13:53:33 +00:00
transifex-integration[bot]
1568e64d55
Updates for po/en.po in ko (#2201)
* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

* Translate po/en.po in ko

100% translated source file: 'po/en.po'
on 'ko'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-06-03 13:50:17 +00:00
transifex-integration[bot]
d08f217b3a
Updates for po/en.po in fr_FR (#2202)
Translate po/en.po in fr_FR

100% translated source file: 'po/en.po'
on 'fr_FR'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-06-03 13:50:02 +00:00
transifex-integration[bot]
29f47a4413
Translations for po/en.po in es (#2197)
Translate po/en.po in es

100% translated source file: 'po/en.po'
on the 'es' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-05-30 08:43:35 +00:00
smolx
35ee42d343
Remove redundant attempts to set install reasons (#2196)
Redundant attempts took place when the installation by pacman was
exited with code 1, i.e. an real error occurred or installation
was simply cancelled.
2023-05-30 08:40:57 +00:00
moson-mo
1335e9b4e0
fix(pkgbuild): convert package name for gitlab URLs (#2191)
Convert package names to gitlab repo names

Signed-off-by: moson-mo <mo-son@mailbox.org>
2023-05-25 11:00:33 +02:00
Jo
e28319fece
chore(yay): lift legacy engine (#2189)
* remove legacy engine

* remove legacy dep handlers

* use prepare for gendb

* remove unused method

* remove aur client old interface

* remove unused menu fns

* remove inactive upgrademenu option

* unexport printInfo
2023-05-24 08:22:18 +00:00
smolx
c1aa71bee1
Fix AUR dependency resolving (#2169)
Before this fix dependencies for AUR targets were added to the graph
after each addition of a target node. Now dependencies are added only
after all target nodes are added to the graph.

Also added some tests for previously bugged cases.
2023-05-23 21:18:41 +00:00
smolx
56d1b7ed1c
Fix --rebuild option (#2163)
* Reimplement --rebuild option in the new engine (#2153)

* Refactor --rebuild option

* Fix comment formatting
2023-05-23 21:16:27 +00:00
transifex-integration[bot]
036a53882d
Translations for po/en.po in sv (#2188)
Translate po/en.po in sv

100% translated source file: 'po/en.po'
on the 'sv' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-05-23 21:08:20 +00:00
transifex-integration[bot]
330b9ab920
Translations for po/en.po in id (#2187)
Translate po/en.po in id

100% translated source file: 'po/en.po'
on the 'id' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-05-23 15:43:27 +02:00
transifex-integration[bot]
98d5352b78
Translations for po/en.po in ca (#2186)
Translate po/en.po in ca

100% translated source file: 'po/en.po'
on the 'ca' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-05-23 11:58:15 +02:00
Jo
27f336e68c
chore(deps): update deps (#2184)
update deps
2023-05-23 07:41:58 +00:00
transifex-integration[bot]
23937356eb
Translations for po/en.po in it_IT (#2183)
Translate po/en.po in it_IT

100% translated source file: 'po/en.po'
on the 'it_IT' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-05-23 09:22:10 +02:00
Jo
9641d2a608
chore(yay): add warning for yay -c (#2181)
add warning for yay -c
2023-05-22 22:02:14 +00:00
Jo
df3dbfa125
chore(yay): update po (#2180)
* update po

* avoid extra translate work
2023-05-22 21:52:06 +00:00
Jo
fdcf6ef664
ci(yay): add integration test framework (#2178)
* add integration test framework

* fix integration tests

* fix integration tests
2023-05-22 21:38:02 +00:00
Jo
39f0d4e9a1
chore(yay): remove unused constant (#2179)
remove unused constant

Co-authored-by: christian-heusel <christian@heusel.eu>
2023-05-22 20:59:30 +00:00
Andrew Geng
d33bf8841d
Fix yay -Sc wiping ~/.cache/yay on 3rd question. (#2175)
If you answer yes to
    :: Do you want to remove all other AUR packages from cache? [Y/n]
then we run cleanAUR(), intending to remove subdirectories of
~/.cache/yay that do not share a name with installed packages not
found in the sync repositories.

Where this was going wrong was cleanAUR() was getting an empty map from
dbExecutor.InstalledRemotePackages()---because InstalledRemotePackages
only recomputes its result if installedRemotePkgMap is nil, whereas
NewExecutor initialized it to an empty map. The symptom was it emptied
my ~/.cache/yay.

We do want a non-nil, empty installedRemotePkgMap to block recomputing
(that is, to indicate the user really has no remote packages), so now
NewExecutor initializes it to nil, and getPackageNamesBySource is
responsible for making sure it's non-nil before writing to it.

Fixes #2152, which seems to have been introduced in
4626a0409c4d34bcffe0d5ed499ebff893115c69.
2023-05-22 20:34:51 +00:00
Jo
bd79057fd9
feat(pkgbuild): use gitlab git repo for retrieving PKGBUILDs (#2177)
use gitlab git repo for retrieving PKGBUILDs
2023-05-22 20:22:49 +00:00
smolx
a0a5e45fe7
Fix -Qu exit code for empty update lists (#2162)
Fix -Qu exit code for empty update lists (#2061)

Previously, -Qun and -Qum without available updates could exit
with code 0 in some cases.
Also fix present and add more tests for such cases.
2023-05-22 18:35:27 +00:00
smolx
ec15a5b363
Implement tests for -Qu/q (#2156)
Implement tests for -Qu/q (#2061)
2023-05-15 10:33:12 +02:00
Jo
d568a73ab8
fix(metadata): reduce cache validity (#2161)
fix metadata cache validity too long
2023-05-15 08:30:41 +00:00
Joaquim Monteiro
490ebe4f7f
Fix formatting of error message that occurs on AUR errors (#2154)
fix: fix formatting of error message that occurs on AUR errors
2023-05-08 21:43:50 +00:00
transifex-integration[bot]
4dfee1f82f
Translate 'po/en.po' in 'fr_FR' (#2150)
Translate po/en.po in fr_FR

100% translated for the source file 'po/en.po'
on the 'fr_FR' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-05-04 16:14:26 +00:00
transifex-integration[bot]
5cf215a00a
Translate 'po/en.po' in 'es' (#2151)
Translate po/en.po in es

100% translated for the source file 'po/en.po'
on the 'es' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-05-04 16:14:15 +00:00
Jo
f7731d7cf9
fix(groups): fix sync group getting passed to reason setter (#2148)
fix sync group install #2137
2023-05-01 16:21:40 +00:00
Jo
966bfb74ee
feat(dep): add provider resolution on first layer (#2147)
add provider resolution on first layer
2023-05-01 15:56:42 +00:00
Jo
c721fe7f3b
fix(search): fix -Si crash when using metadata engine (#2146)
fix missing By query in metadata search
2023-05-01 12:52:43 +00:00
Jo
b51c10ca3e
doc(yay): specify search narrowing in man (#2145)
specify search narrowing in man
2023-05-01 12:33:19 +00:00
Tanmay Chaudhry
4832ec59db
Fix version diff word detection (#2124)
* Fix version diff word detection

* remove double negation

* preserve text color after tests
2023-04-27 07:23:25 +00:00
Jo
49267b9cd9
feat(upgrade): separate menu for pulled along dependencies (#2141)
try separate menu for pulled along

use installed as term

fix order gap

fix tests

add aur db + aur scenario
2023-04-27 07:20:21 +00:00
Jo
e6344100e6
fix(upgrade): fix local pulled dependencies replacing upgrade information (#2140)
avoid upgrade info loss

fix colormap
2023-04-27 07:04:09 +00:00
Tanmay Chaudhry
15400c5fc5
Switch the TZ to UTC temporarily for running the News tests (#2121)
* switch the TZ to UTC temporarily for running the tests

* Remove unnecessary environment reset

* Set test specific environment.

Co-authored-by: Jo <me@jguer.space>

---------

Co-authored-by: Jo <me@jguer.space>
2023-04-26 13:54:37 +02:00
transifex-integration[bot]
822b11b4d6
Translate 'po/en.po' in 'pt' (#2133)
Translate po/en.po in pt

100% translated for the source file 'po/en.po'
on the 'pt' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-04-26 13:53:54 +02:00
transifex-integration[bot]
71432a447e
Translate 'po/en.po' in 'cs' (#2130)
Translate po/en.po in cs

100% translated for the source file 'po/en.po'
on the 'cs' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-04-26 13:53:29 +02:00
transifex-integration[bot]
957292a911
Translate 'po/en.po' in 'zh_TW' (#2138)
Translate po/en.po in zh_TW

100% translated for the source file 'po/en.po'
on the 'zh_TW' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-04-26 13:46:06 +02:00
transifex-integration[bot]
8c69356bd4
Translate 'po/en.po' in 'it_IT' (#2126)
Translate po/en.po in it_IT

100% translated for the source file 'po/en.po'
on the 'it_IT' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-04-20 08:58:19 +02:00
transifex-integration[bot]
64bb346fbe
Translate 'po/en.po' in 'de' (#2119)
* Translate po/en.po in de

100% translated for the source file 'po/en.po'
on the 'de' language.

* Translate po/en.po in de

100% translated for the source file 'po/en.po'
on the 'de' language.

* Translate po/en.po in de

100% translated for the source file 'po/en.po'
on the 'de' language.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-04-16 16:23:42 +00:00
transifex-integration[bot]
797b7485ab
Translate 'po/en.po' in 'sv' (#2118)
* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-04-16 16:23:27 +00:00
Jo
57a3a090f1
fix(vcs): improve timeout handling for vcs upgrade check (#2120)
remove timeout from full operation and increase timeout for single checking
2023-04-16 16:23:10 +00:00
transifex-integration[bot]
f0bfe63ced
Translate 'po/en.po' in 'ko' (#2116)
* Translate po/en.po in ko

100% translated for the source file 'po/en.po'
on the 'ko' language.

* Translate po/en.po in ko

100% translated for the source file 'po/en.po'
on the 'ko' language.

* Translate po/en.po in ko

100% translated for the source file 'po/en.po'
on the 'ko' language.

* Translate po/en.po in ko

100% translated for the source file 'po/en.po'
on the 'ko' language.

* Translate po/en.po in ko

100% translated for the source file 'po/en.po'
on the 'ko' language.

* Translate po/en.po in ko

100% translated for the source file 'po/en.po'
on the 'ko' language.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-04-13 06:50:20 +00:00
Jo
83214fbc1c
chore(yay): add local newer to AUR warnings (#2113) 2023-04-11 21:15:47 +00:00
Jo
161fede450
chore(yay): use new aur client for -Si (#2112)
* chore(yay): use new aur client for -Si

* chore(yay): use new client for clean
2023-04-11 21:15:21 +00:00
Jo
26c9ab5a87
fix(yay): fix panic -Si when package is missing (#2111)
fix panic -Si
2023-04-11 16:45:20 +00:00
Jo
4a9c736e2a
chore(upgrade): add makedep explain to the upgrade menu (#2110)
* display required by

* cutoff at 2
2023-04-11 16:41:34 +00:00
adasauce
76e5ee1fa6
fix: querybuilder GetTargets start iteration at 1, when include is 0 causes a crash (#2109) 2023-04-11 16:23:19 +00:00
Jo
88008e4eb3
feat(yay): skip confirmed confirms (#2107)
* skip pacman confirmations when yay confirmations are done

* default to double confirm

* fix tests
2023-04-11 11:51:39 +00:00
Jo
c7a51a1614
fix(text): ensure error logs go to stderr (#2105)
ensure Error logs go to stderr
2023-04-10 16:26:09 +00:00
transifex-integration[bot]
6a971df635
Translate 'po/en.po' in 'ca' (#2100)
* Translate po/en.po in ca

100% translated for the source file 'po/en.po'
on the 'ca' language.

* Translate po/en.po in ca

100% translated for the source file 'po/en.po'
on the 'ca' language.

* Translate po/en.po in ca

100% translated for the source file 'po/en.po'
on the 'ca' language.

* Translate po/en.po in ca

100% translated for the source file 'po/en.po'
on the 'ca' language.

* Translate po/en.po in ca

100% translated for the source file 'po/en.po'
on the 'ca' language.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-04-10 15:47:31 +00:00
Jo
511b95769e
docs(yay): correct order of editor var evaluation (#2104)
correct documentation on order of editor preference. Use logger for editfn
2023-04-10 15:46:46 +00:00
transifex-integration[bot]
4e9a865388
Translate 'po/en.po' in 'id' (#2092)
Translate po/en.po in id

100% translated for the source file 'po/en.po'
on the 'id' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-04-08 07:58:54 +00:00
transifex-integration[bot]
9270f00c7e
Translate 'po/en.po' in 'pt' (#2093)
Translate po/en.po in pt

100% translated for the source file 'po/en.po'
on the 'pt' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-04-08 07:58:37 +00:00
transifex-integration[bot]
527c3a5058
Translate 'po/en.po' in 'tr' (#2089)
* Translate po/en.po in tr

100% translated for the source file 'po/en.po'
on the 'tr' language.

* Translate po/en.po in tr

100% translated for the source file 'po/en.po'
on the 'tr' language.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-04-06 20:57:20 +00:00
Jo
1ee94f28d3
fix(new_engine): add missing warnings to AUR updates (#2087)
* add missing warnings to AUR updates

* fix tests
2023-04-06 17:06:46 +00:00
Jo
a64180464b
chore(man): fix wording on operation order to make it more clear (#2088)
fix wording on operation order to make it more clear
2023-04-06 16:49:18 +00:00
transifex-integration[bot]
a31ca0d7dc
Translate 'po/en.po' in 'it_IT' (#2084)
* Translate po/en.po in it_IT

100% translated for the source file 'po/en.po'
on the 'it_IT' language.

* Translate po/en.po in it_IT

100% translated for the source file 'po/en.po'
on the 'it_IT' language.

* Translate po/en.po in it_IT

100% translated for the source file 'po/en.po'
on the 'it_IT' language.

* Translate po/en.po in it_IT

100% translated for the source file 'po/en.po'
on the 'it_IT' language.

* Translate po/en.po in it_IT

100% translated for the source file 'po/en.po'
on the 'it_IT' language.

* Translate po/en.po in it_IT

100% translated for the source file 'po/en.po'
on the 'it_IT' language.

* Translate po/en.po in it_IT

100% translated for the source file 'po/en.po'
on the 'it_IT' language.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-04-06 16:32:53 +00:00
transifex-integration[bot]
1bc3171abd
Translate 'po/en.po' in 'sv' (#2075)
* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

* Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-04-06 16:32:45 +00:00
Jo
2d7297ae6d
fix(upgrades): don't include makedeps into -Qu/a (#2085)
don't include makedeps into -Qu/a
2023-04-06 16:32:22 +00:00
Jo
ce0cb35510
feat(new_engine): add support for --nocombinedupgrade (#2083)
* feat(new_engine): add support for --nocombinedupgrade

* update en.po
2023-04-06 11:54:03 +00:00
transifex-integration[bot]
d9b57790fa
Translate 'po/en.po' in 'pt' (#2081)
Translate po/en.po in pt

100% translated for the source file 'po/en.po'
on the 'pt' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-04-06 11:45:53 +00:00
Jo
5a3f277574
chore(yay): make warning less scary (#2074) 2023-04-05 11:53:11 +00:00
Jo
d1c012085c
fix(yay): ensure yay -Qu & -Quq don't get stuck looking for deps (#2072) 2023-04-05 11:43:30 +00:00
Jo
89f47f8ebe
fix(yay): correct operation order for preparer (#2071)
* fix(yay): reset to origin/HEAD for clean_menu

* fix(yay): correct operation order for preparer

* test(yay): fix flaky test
2023-04-05 11:27:42 +00:00
transifex-integration[bot]
5b5617c7e7
Translate 'po/en.po' in 'it_IT' (#2065)
Translate po/en.po in it_IT

100% translated for the source file 'po/en.po'
on the 'it_IT' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-04-05 10:36:52 +02:00
transifex-integration[bot]
b80ef15add
Translate 'po/en.po' in 'sv' (#2063)
Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-04-05 10:36:42 +02:00
transifex-integration[bot]
e0fbb4495a
Translate 'po/en.po' in 'id' (#2068)
Translate po/en.po in id

100% translated for the source file 'po/en.po'
on the 'id' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-04-05 10:36:35 +02:00
jguer
5c7f9ba159
update en.po 2023-04-04 00:14:39 +02:00
Jo
0892fc7cdd
fix(query): reduce verbosity of -Qu (#2060) 2023-04-03 22:05:45 +00:00
jguer
c78d031b32
Revert "ci(yay): execute release-notary publish on ci (#2056)"
This reverts commit 6983a9ee7e760764df1fe7a25b3f5aa9ca3341dc.
2023-04-03 18:54:19 +02:00
Jo
6983a9ee7e
ci(yay): execute release-notary publish on ci (#2056) 2023-04-03 16:49:00 +00:00
Jo
ada8261bca
fix(yay): check if downgrade has smaller version (#2054) 2023-04-03 16:37:41 +00:00
Jo
3f09397816
Update bug_report.md (#2053) 2023-04-03 16:34:39 +00:00
Jo
9532e7b7da
fix(yay): show yay usage always on --help -h (#2051)
fix yay usage not showing
2023-04-03 13:50:49 +02:00
bittin
956c4cb100
Add bittin as translator (#2050) 2023-04-03 11:10:10 +00:00
Jo
16cce4384b
ci(lint): update builder image (#2048)
* last min ci changes

* readme update
2023-04-03 07:32:25 +00:00
Jo
c3888d9881
chore(ci): add missing translation files to release package (#2047)
add missing pos
2023-04-03 07:25:59 +00:00
Jo
c63576c36d
fix(search): always trust search if there's only one term (#2046)
always trust search if there's only one term
2023-04-03 07:16:03 +00:00
Jo
dd42593ba1
fix(search): only do exact trim in certain modes (#2045)
only do exact trim in certain modes
2023-04-02 17:50:35 +00:00
Jo
d13bdb0ce1
feat(search): improve exact match for separate source (#2044)
* unify query builder

* remove uneeded code

* reorganize code
2023-04-02 00:23:02 +00:00
Jo
6390d1c2b0
chore(query_builder): tweak mixed sources similarity matcher. related to #1719 (#2043)
tweak mixed sources similarity matcher. related to #1719
2023-04-01 13:30:50 +00:00
Jo
9028f5d8be
ci(release): update release notary (#2042)
update release notary
2023-04-01 10:48:46 +00:00
Jo
5d1c54413c
fix(query_builder): use correct aur client for mixed query builder (#2041)
* use same repo search as pacman

use logger child from runtime

* use common interface for aur clients
2023-04-01 10:33:05 +00:00
Jo
e615f8e07e
fix(new_engine): exclude menu removing new deps (#2040)
fix exclude menu removing new deps
2023-03-31 22:15:57 +00:00
Jo
d75e0a001d
fix(clean): modify clean args (#2039) 2023-03-31 21:22:57 +00:00
Ferdinand Bachmann
2bdbc3e06b
fix(aur_install): fix debug packages being added to deps even if not found (#2038) 2023-03-30 21:48:33 +00:00
Jo
01666aef37
fix(vcs): add extra context to errors and increase timeouts (#2037)
* give a more complete message on vcs error

* bump timeouts for vcs checking
2023-03-30 18:57:56 +00:00
transifex-integration[bot]
68337a58c1
Translate 'po/en.po' in 'it_IT' (#2035)
Translate po/en.po in it_IT

100% translated for the source file 'po/en.po'
on the 'it_IT' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-03-30 17:08:28 +00:00
transifex-integration[bot]
ca12cd7156
Translate 'po/en.po' in 'tr' (#1993)
Translate po/en.po in tr

100% translated for the source file 'po/en.po'
on the 'tr' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-03-29 11:47:47 +02:00
Ferdinand Bachmann
bebe80bb84
fix(aur_install): fix debug pacakges not being found (#1977) 2023-03-29 09:08:25 +00:00
transifex-integration[bot]
9137c1e95f
Translate 'po/en.po' in 'zh_CN' (#1978)
Translate po/en.po in zh_CN

100% translated for the source file 'po/en.po'
on the 'zh_CN' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-03-29 09:06:59 +00:00
transifex-integration[bot]
bdd888c59d
Translate 'po/en.po' in 'ko' (#1992)
Translate po/en.po in ko

100% translated for the source file 'po/en.po'
on the 'ko' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-03-29 09:06:38 +00:00
transifex-integration[bot]
16b9516f96
Translate 'po/en.po' in 'fr_FR' (#1974)
Translate po/en.po in fr_FR

100% translated for the source file 'po/en.po'
on the 'fr_FR' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-03-15 09:34:10 +01:00
Jo
dfa7ed51c1
chore(yay): change found repo to debug (#1966)
change found to debug
2023-03-13 08:55:08 +00:00
Jo
7bc4a666e6
refactor(runtime): Build runtime after cmdargs parsing (#1965)
* extract runtime building from cfg

* respect AURRPCURL

* use -Syu if there are no targets, allows to pass extra options

* one more step towards removing runtime from cfg
2023-03-13 08:48:39 +00:00
transifex-integration[bot]
210512a5d6
Translate 'po/en.po' in 'ko' (#1963)
Translate po/en.po in ko

100% translated for the source file 'po/en.po'
on the 'ko' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-03-12 15:49:31 +00:00
transifex-integration[bot]
57250fec4b
Translate 'po/en.po' in 'sv' (#1962)
Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-03-12 15:49:23 +00:00
transifex-integration[bot]
e56b9cd72b
Translate 'po/en.po' in 'id' (#1960)
Translate po/en.po in id

100% translated for the source file 'po/en.po'
on the 'id' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-03-11 13:16:10 +00:00
transifex-integration[bot]
794a38fa28
Translate 'po/en.po' in 'pt' (#1959)
Translate po/en.po in pt

100% translated for the source file 'po/en.po'
on the 'pt' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-03-10 12:10:57 +01:00
Jo
f11f9058c2
fix(logger): respect --debug for setting debug logger (#1955)
respect --debug parse
2023-03-08 21:33:43 +00:00
Jo
7e7764a797
chore(new_engine): use new engine for -Qu (#1954)
* use new engine for -Qu

* fix devel search showing up in -Quq

* test empty upgrade menu
2023-03-08 21:30:54 +00:00
Jo
c744058b20
Update manual page and remove deprecate/removed options (#1951)
* add new options and remove deprecated

* add new -Bi
2023-03-08 13:07:15 +00:00
transifex-integration[bot]
7073939cdc
Translate 'po/en.po' in 'ca' (#1950)
Translate po/en.po in ca

100% translated for the source file 'po/en.po'
on the 'ca' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-03-08 10:47:21 +01:00
Jo
3d5a43c294
chore(yay): bump version (#1949)
bump major
2023-03-07 21:04:06 +00:00
transifex-integration[bot]
46bf36a160
Translate 'po/en.po' in 'sv' (#1948)
Translate po/en.po in sv

100% translated for the source file 'po/en.po'
on the 'sv' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-03-07 20:48:08 +00:00
Jo
463e60e045
chore(deps): update goalpm (#1947)
* update dependencies

* use logger in cmd runner
2023-03-06 09:19:21 +00:00
Jo
e6ed869df1
chore(yay): remove global cfg (#1946)
remove global cfg
2023-03-05 21:58:18 +00:00
Jo
8b8d6001a4
fix(new_engine): Improve partial upgrade protection and pinned deps (#1945)
* fix dep graph, existing in graph

* do not change from same dep reason

* roll up layer installs in case of fail

* re-use pacman exclude mechanism

should finish the reimplementation of the missing guards from the legacy
engine.

* include update in debug log

* test rollups
2023-03-05 17:31:11 +00:00
Joey H
0387dfdb59
fix(install): use global arguments when removing make dependencies (#1940)
fix(install): use global arguments when removing make dependencies

One example where this would fail before is when the `--root` argument
was passed in by the user. Yay would install the make dependencies to
the new root directory, but would try to remove them from / causing the
installation to fail if the make dependencies were not found in the
default installation path (/).
2023-02-26 11:25:02 +00:00
Jo
fa2e726ca6
chore(text): use logger in new engine services (#1939)
* use logger in vcs

* use logger in query builder

* use logger in migrations
2023-02-25 19:03:27 +00:00
Jo
841395c318
feat(local_install): check PKGBUILD and .SRCINFO presence and generate .SRCINFO if necessary (#1938)
check build file presence and generate if needed
2023-02-25 17:44:24 +00:00
Jo
4e0a5c8520
feat(new_engine): respect -w in AUR package building (#1923)
respect -w in AUR package building
2023-02-21 02:49:48 +00:00
Jo
61f1bdf291
fix(cmd): propagate sigterm to spawned processes (#1927)
propagate sigterm to spawned processes
2023-02-21 02:49:33 +00:00
Jo
3ef4664d99
fix(devel): timeout devel check after 5 secs (#1929)
timeout devel check after 5 secs
2023-02-21 02:48:56 +00:00
Jo
4780a974d9
fix flaky tests (#1928) 2023-02-21 02:42:56 +00:00
Jo
7c8f273cdf
fix(git): add git denylist for workspace variables (#1926)
add git denylist for workspace variables
2023-02-21 02:23:48 +00:00
Jo
a3d51a42da
fix(yay): match -Qu return (#1924)
match -Qu return
2023-02-21 02:00:39 +00:00
jguer
096ff7a544
fix -Si aur & repo mix 2023-02-21 02:55:49 +01:00
Jo
6c870db1f1
fix(aur): respect -uu for AUR downgrade (#1925)
respect -uu for AUR downgrade
2023-02-21 01:51:52 +00:00
Jo
f0433cc339
feat(metadata): respect regex in search (#1922)
respect regex in search
2023-02-21 01:24:48 +00:00
Jo
fad26c078d
fix(getpkgbuild): check AUR pkgs exist before GetPKGBUILD (#1921)
check AUR pkgs exist before GetPKGBUILD
2023-02-20 23:58:13 +00:00
Jo
7490836991
fix(new_engine): respect --needed on target gathering (fixes #1552) (#1920)
* use logger in dep graph

* use logger in dep graph

* use logger in dep graph

* only query for AUR packages once per tier. useful for rpc

* fix performance regression for ros-melodic

* prefer name search first

* implement needed at target gathering

* set default config

* fixup tests for needed
2023-02-20 23:14:59 +00:00
Jo
8d18f1be18
fix(new_engine): add aur client support to mixed source display (#1919)
* update aur client and use correct aur client

* add aur query client support for mixed source engine
2023-02-20 12:14:16 +00:00
Jo
f1d086df1d
Allow use of rpc client as an alternative to the metadata client (#1918)
* use updated aur client

* add logger to rpc client

* update go.mod
2023-02-20 11:20:48 +00:00
Jo
2f5fd5cb1c
fix(new_engine): add missing version for devel packages in combi (#1917)
* fix missing latest-commit in devel upgrade menu

* move test grapher to inner test
2023-02-20 09:51:39 +00:00
Jo
0a8bc1fe2e
fix(new_engine): add missing latest-commit in devel upgrade menu (#1916)
* fix missing latest-commit in devel upgrade menu

* move test grapher to inner test
2023-02-17 19:29:46 +00:00
Jo
0bf4c2e502
feat(new_install): show (#1915)
* show new packages in upgrade form if they exist

* refactor up select

* remove unused graph parts

* readd len

* Complete upgrade graphing

* Extract to upgrade pkg

* remove unused dep method

* remove uneeded dep

* cleanup method

* specify io Reader for testing

* use specified input vector

* fix non-active devel

* test base cases

* add devel test cases

* add range tests

* add logger struct

* use logger struct in upgrade

* follow golangci recommendations

* update deps

* update golangci
2023-02-17 19:01:26 +00:00
Jo
4f50b799ef
feat(local_install): add choice menu for yay -Bi (#1903)
add choice menu for yay -Bi
2023-01-23 23:54:15 +00:00
Jo
4626a0409c
fix(vcs): do not vcs update gather orphan info (#1902)
* reduce complexity of devel upgrade gathering

* clean orphans devel
2023-01-23 23:03:32 +00:00
Jo
1bfbd01f94
fix(sync): do not update vcs info of failed packages (#1901)
* extract srcinfo service to pkg

* take into account failed installs for vcs update. Fixes #1892

* fix tests
2023-01-23 21:43:58 +00:00
transifex-integration[bot]
04c4b0aa59
Translate '/po/en.po' in 'ru' (#1898)
Translate /po/en.po in ru

translation completed for the source file '/po/en.po'
on the 'ru' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-01-23 17:03:14 +01:00
Jo
9356481d1c
fix(config): expand tilde for some config fields. Fixes #1774 (#1897)
expande tilde for some fields. Fixes #1774
2023-01-22 23:21:05 +00:00
transifex-integration[bot]
2f1ebb9fde
Translate '/po/en.po' in 'tr' (#1888)
Translate /po/en.po in tr

translation completed for the source file '/po/en.po'
on the 'tr' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
Co-authored-by: jguer <me@jguer.space>
2023-01-19 10:51:43 +01:00
transifex-integration[bot]
5626ed3ff4
Translate '/po/en.po' in 'he' (#1895)
Translate /po/en.po in he

translation completed for the source file '/po/en.po'
on the 'he' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-01-18 11:46:45 +01:00
Balki
e09209bb19
fix(zsh): fix zsh completion for yay specific flags (#1893) 2023-01-13 13:50:09 +00:00
Jo
c40e949752
fix(build): fix issue where shell conditional output is passed to go build (#1887)
fix issue where shell conditional output is passed to go build

(cherry picked from commit 6744fa721f4737f695b40eac0571e3b4b1504b65)
2023-01-05 15:50:53 +00:00
Jo
b8debd1ae7
chore(yay): fix small linting issues (#1885)
* replace context.TODO() in tests context.Background()

* remove mock TODOs

* prettier

* apply missing linting
2023-01-03 21:43:56 +00:00
Joey H
8948278568
fix(parser): ensure data is piped when using '-' argument (#1881)
fix(parser): ensure data is piped when using '-' argument

Fixes #1626.

Check to see if data was actually piped to yay, otherwise it will hang
forever. This error will give users better feedback of how to use the
'-' argument (it is also the same exact error pacman throws for invalid
piped data).

Two tests were added. However, I didn't know how to add a test for the
actual part that detects whether data was piped or not (which is
essentially what this commit adds).
2023-01-03 19:43:15 +00:00
Jo
86bba8a289
fix(ci): run ci on all PRs (#1884)
* fix devel sysupgrade

* fix ci stuck on .po
2023-01-03 19:35:44 +00:00
transifex-integration[bot]
47b1428a25
Translate '/po/en.po' in 'fr_FR' (#1882)
Translate /po/en.po in fr_FR

translation completed for the source file '/po/en.po'
on the 'fr_FR' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-01-03 19:30:47 +00:00
Jo
b3c334e014
fix(new_engine): fix devel sysupgrade (#1883)
fix devel sysupgrade
2023-01-03 19:24:28 +00:00
transifex-integration[bot]
b41e67f31e
Translate '/po/en.po' in 'sv' [manual sync] (#1876)
Translate /po/en.po in sv

at least 90% translated for the source file '/po/en.po'
on the 'sv' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-01-02 19:06:30 +01:00
transifex-integration[bot]
13109992c5
Translate '/po/en.po' in 'zh_CN' [manual sync] (#1877)
Translate /po/en.po in zh_CN

at least 90% translated for the source file '/po/en.po'
on the 'zh_CN' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-01-02 19:04:56 +01:00
transifex-integration[bot]
ed95688c1b
Translate '/po/en.po' in 'zh_TW' [manual sync] (#1878)
Translate /po/en.po in zh_TW

at least 90% translated for the source file '/po/en.po'
on the 'zh_TW' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-01-02 19:04:32 +01:00
transifex-integration[bot]
b8b085599a
Translate '/po/en.po' in 'fr_FR' [manual sync] (#1879)
Translate /po/en.po in fr_FR

at least 90% translated for the source file '/po/en.po'
on the 'fr_FR' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-01-02 19:03:50 +01:00
transifex-integration[bot]
7a2db4f448
Translate '/po/en.po' in 'it_IT' [manual sync] (#1880)
Translate /po/en.po in it_IT

at least 90% translated for the source file '/po/en.po'
on the 'it_IT' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-01-02 19:02:54 +01:00
transifex-integration[bot]
40f058fd19
Translate '/po/en.po' in 'cs' [manual sync] (#1873)
Translate /po/en.po in cs

at least 90% translated for the source file '/po/en.po'
on the 'cs' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-01-02 19:00:45 +01:00
transifex-integration[bot]
f771424336
Translate '/po/en.po' in 'de' [manual sync] (#1874)
Translate /po/en.po in de

at least 90% translated for the source file '/po/en.po'
on the 'de' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-01-02 18:59:38 +01:00
transifex-integration[bot]
36282a8192
Translate '/po/en.po' in 'id' [manual sync] (#1875)
Translate /po/en.po in id

at least 90% translated for the source file '/po/en.po'
on the 'id' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-01-02 18:58:51 +01:00
Jo
f372494d74
feat(search): add new bys and misc fixes (#1870)
* use default bin entry of gpg

* fix(dep): fix displayed information in chosen provide

* add new rpc bys to searchby

* man document
2022-12-30 19:02:32 +00:00
Jo
9be51052f7
update deps (#1868)
* update deps

* align struct
2022-12-29 18:42:43 +00:00
Jo
28d90c981e
feat(new engine): local install feature testing (#1867)
* make config into parameter

* test(new engine): local install test

* test(keys): fix test keys

* complete integration test for local install

* add simple mising mechanism
2022-12-29 12:34:53 +00:00
Joey H
d3fbfa26ca
fix(pre-commit): update URL and versions in the pre-commit configuration (#1865)
There was a dead link in the pre-commit-golang URL. Also, Woile/commitizen redirects to commitizen-tools/commitizen, so change that URL as well. Last, update all the versions using pre-commit autoupdate.

This will make it easier for new contributers to use the pre-commit
hooks when contributing.
2022-12-27 18:56:28 +00:00
Joey H
8c61bc9b45
fix(cmd): pass install flags into pacman in yogurt mode (#1864)
Fixes #1560
2022-12-27 18:55:42 +00:00
Joan Bruguera
2bda76e431
fix(clean): notify RemoveAll error when cleaning AUR (#1863)
otherwise if any hard-to-delete file lands in the AUR cache folder, running
`yay -Scc` will appear to succeed to the user, but actually abort midway.
2022-12-21 10:09:59 +01:00
Jo
9a23b792c4
Improve warning messages (#1857)
improve warning messages
2022-12-19 18:17:47 +00:00
Jo
4e3c664ab3
ci(yay) : attempt to use caches more effectively in CI (#1862)
* attempt to use caches more effectively

* fix cache key

* specify cache file
2022-12-18 20:42:22 +00:00
Jo
3eb9eb0d3d
feat(new_engine): add support for noDeps and noCheckDeps (#1861)
* feat(new engine): add support for noDeps and noCheckDeps

* test(new engine): Add tests for -dd and normal split package resolution
2022-12-18 20:14:41 +00:00
Jo
0b1ae938a3
fix(ci): do not install packages at pacman-git CI level (#1860)
* fix(ci): do not attempt to install extra packages for building pacman-git

* install needed in ci image

* fix missing pacman key

* wip

* add missing deps
2022-12-18 18:24:56 +00:00
Jo
f8e7891b0b
refactor(vcs): remove mux and use interface for other packages (#1859)
* refactor(vcs): remove context passing mutex from VCS interface

* simplify devel upgrade gather

* update vcs upgrade tests

* remove unused mock
2022-12-18 16:37:15 +00:00
Jo
4a3e365fc5
AUR provides search rpc (#1856)
* add support for provides search in AUR RPC client

* return provides search to default true
2022-12-18 02:36:49 +01:00
Jo
27cbbf4cb9
tests(new engine): add tests for layering and source handling (#1854)
* add tests for layering and source handling

* add tests for jellyfin split package
2022-12-18 02:00:39 +01:00
Jo
7da9f4869d
feat(new engine): skip built and respect --needed (#1852)
* add built package check

* respect --needed for new engine

* add needed check and test

* add test for not built
2022-12-16 17:23:44 +00:00
transifex-integration[bot]
c826456d4d
Translate '/po/en.po' in 'ko' (#1851)
Translate /po/en.po in ko

translation completed for the source file '/po/en.po'
on the 'ko' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-12-12 09:40:33 +00:00
transifex-integration[bot]
9df087bd3d
Translate '/po/en.po' in 'pt' (#1845)
Translate /po/en.po in pt

translation completed for the source file '/po/en.po'
on the 'pt' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-12-06 18:19:59 +01:00
Jo
1b5f0d66fe
Fix legacy engine install issues (#1842)
* fix debug pkg clearing pkg archive slice

* add debug messages and limit check for srcinfo

* treat pkgbuildDirs as expected directory, not proven directory

Co-authored-by: David Tomaschik <david@systemoverlord.com>

Co-authored-by: David Tomaschik <david@systemoverlord.com>
2022-11-29 13:23:52 +00:00
Jo
4f1f539217
Ignore last layer fails and keep building (#1841)
* fix: do not instantiate cleaning hooks if there's no AUR pkg

* chore: squash local and sync install

* still update completions in local mode

* allow failures on the last install layer
2022-11-29 12:25:36 +00:00
Jo
7612bb5da5
Mutualize local sync install (#1836)
* fix: do not instantiate cleaning hooks if there's no AUR pkg

* chore: squash local and sync install

* still update completions in local mode
2022-11-20 13:02:01 +00:00
Jo
9f67d10d5c
feat(v12): add group install (#1835)
v12engine: add group install
2022-11-20 02:47:23 +00:00
Jo
6ad63cae10
fix: rework menus to work on both flows (#1830)
* rework menus to work on both flows

* add installed package split

* remove unused field

* Add post install hooks
2022-11-20 00:51:55 +00:00
transifex-integration[bot]
63f20599cd
Translate '/po/en.po' in 'ca' (#1833)
Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-11-19 19:18:49 +00:00
Jo
0f45a99efa
yay v12: new AUR query engine, Local PKGBuild install, debug mode, new install engine + bugs (#1826)
yay v12: new AUR query engine, Local PKGBuild install, debug mode, new install engine + bugs (+bugs)
2022-11-16 01:32:26 +00:00
jguer
f52db80a9d
po update 2022-11-16 02:21:35 +01:00
jguer
7f151cd603
move addUpgradeToGraph to depgraph 2022-11-16 01:11:16 +01:00
jguer
2805252365
fix GraphFromTarget bug where empty DB would be used as identifier 2022-11-16 01:03:57 +01:00
jguer
481c63db91
remove local build from -U 2022-11-16 00:56:24 +01:00
jguer
5e8d9ac846
add make to dockerfile 2022-11-16 00:54:21 +01:00
jguer
085e2c8aea
rename -Ui to -Bi 2022-11-16 00:51:57 +01:00
jguer
1153ee6dbb
support multiple targets. Remove working directory logic 2022-11-16 00:45:59 +01:00
jguer
3f5eb36fe2
add gcc to build 2022-11-16 00:29:53 +01:00
jguer
2358a7f66e
use aur package metadata client 2022-11-16 00:25:17 +01:00
jguer
2560cadcc0
update aur package 2022-11-15 19:02:35 +01:00
jguer
c00cd8d88e
rename AUR metadata client 2022-11-15 16:22:57 +01:00
jguer
f042713aaa
fix: fix linting errors 2022-11-15 15:44:50 +01:00
jguer
ae918986f4
Merge remote-tracking branch 'origin/next' into jguer/local-pkgbuild-install 2022-11-15 15:19:30 +01:00
Jo
3f5dffc188
build: update golangci (#1829) 2022-11-15 14:17:56 +00:00
jguer
3f7f55f260
simplify src download 2022-11-14 01:14:13 +01:00
jguer
fd46fa0f33
fix failing errors 2022-11-14 00:25:14 +01:00
jguer
d7851223c6
correct nits from metadata package and remove uneeded functionality 2022-11-14 00:21:36 +01:00
jguer
5aeb0d696c
support contains 2022-11-13 23:53:37 +01:00
jguer
56a46644cc
metadata respect search by 2022-11-13 17:47:19 +01:00
jguer
01721c816c
refactor query builder to include AURClient 2022-11-13 14:29:00 +01:00
jguer
cc8c0a2366
fix non-prefixed version 2022-11-10 01:48:58 +01:00
jguer
6cbf00c5a7
typos 2022-11-08 01:37:54 +01:00
jguer
742b6ad79c
use aur cache for upgrades 2022-11-08 01:32:21 +01:00
jguer
9b576fbab7
update dependencies 2022-11-08 01:12:13 +01:00
jguer
bfb32ea63b
update dependencies 2022-11-08 01:11:57 +01:00
jguer
a724d1554f
check hook is not nil 2022-11-03 16:23:47 +01:00
jguer
3fc5d93243
add graph feeding 2022-11-02 00:37:27 +01:00
jguer
0b3ca79788
reduce scope 2022-11-01 23:51:24 +01:00
jguer
b5bdcfbd1a
add basic sync upgrade capabilities 2022-11-01 23:48:35 +01:00
jguer
776fc9686a
Merge remote-tracking branch 'origin/next' into jguer/local-pkgbuild-install 2022-10-28 23:58:23 +02:00
jguer
d3efb59da3
extract upgrade target adder 2022-10-28 23:58:15 +02:00
Jo
a88fad68d4
fix: fix wrong env var in readme for aur voting (#1823) 2022-10-28 20:57:23 +00:00
jguer
849e8f7b60
restore install support for legacy 2022-10-28 01:01:03 +02:00
jguer
ba935ccf95
add support for target install 2022-10-28 00:38:11 +02:00
Norbert Pfeiler
1f49b7e11c
align test numbers, also color expected output (#1815) 2022-10-11 08:39:08 +02:00
jguer
f496dbac8b
error refactor 2022-09-20 00:44:06 +02:00
jguer
bc4732e9e1
add debug mode 2022-09-20 00:11:10 +02:00
jguer
e4fdc9a4d4
readd makedep primitives 2022-09-20 00:01:19 +02:00
moson-mo
647d160182
add option/setting "AurRpcUrl" (#1809)
Signed-off-by: moson-mo <mo-son@mailbox.org>
2022-09-18 00:05:27 +00:00
jguer
c86c460816
fix typos 2022-09-17 14:45:07 +02:00
jguer
d646cd6c87
add base to name map 2022-09-17 14:31:54 +02:00
transifex-integration[bot]
7f9ac3435f
Translate '/po/en.po' in 'ca' (#1813)
Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-09-16 13:52:25 +02:00
jguer
ed94152cfe
first install 2022-09-12 00:18:38 +02:00
jguer
915799755b
allow pkgdest specification 2022-09-12 00:10:19 +02:00
jguer
351e352f64
basic aur install 2022-09-11 23:15:31 +02:00
jguer
5bb46ac1de
add repo install 2022-09-09 20:57:18 +02:00
jguer
4a73dfb0ca
topo sorted map 2022-09-09 17:38:48 +02:00
jguer
be036c39ff
wip 2022-09-08 18:04:51 +02:00
Mudskipper875
cd4462761a
Fix environment variable name in man-page (#1806) 2022-09-07 18:08:15 +02:00
jguer
d53505be37
allow customizing colors and embedding extra info 2022-09-07 01:03:29 +02:00
jguer
0aa18d61d5
cleanup 2022-09-07 00:01:23 +02:00
jguer
95e7542ade
Merge remote-tracking branch 'origin/next' into jguer/local-pkgbuild-install 2022-09-06 23:39:08 +02:00
jguer
cadeecc4df
add dep graph for local install 2022-09-06 23:38:47 +02:00
jguer
f7286b25ae
add local graph util 2022-09-06 23:25:44 +02:00
jguer
650809eba1
wip 2022-09-04 23:45:40 +02:00
davejab
0d1e339fda
Remove package from VCS on failed install (#1802) 2022-09-03 00:42:37 +00:00
linweiyuan
7ebe64871d
fix(yay): fix -Si alignment when language is Chinese (#1799) (#1803) 2022-08-29 15:33:48 +00:00
transifex-integration[bot]
14cc8d4ef0
Translate '/po/en.po' in 'cs' (#1804)
Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-08-29 15:25:22 +00:00
jguer
1d2d19c323
Merge remote-tracking branch 'origin/next' into jguer/local-pkgbuild-install 2022-08-23 23:29:27 +02:00
Jo
70695d7de7
chore(yay): avoid clearing panics in the main func (#1801) 2022-08-23 16:50:17 +00:00
jguer
a4565b367b
wip 2022-08-23 18:33:40 +02:00
jguer
cce21ce0b6
Merge remote-tracking branch 'origin/next' into jguer/local-pkgbuild-install 2022-08-23 17:22:10 +02:00
Jo
714fee0b18
Chore: Cleanup (#1800) 2022-08-22 22:20:09 +00:00
jguer
b054828aa8
wip 2022-08-22 23:28:53 +02:00
jguer
859b7c703f
add local install handle 2022-08-21 07:15:04 +02:00
jguer
446dc86d1e
add handle upgrade 2022-08-21 07:08:01 +02:00
Jordan Cannon
b6b1888e89
Remove duplicate line from man page (#1792)
Co-authored-by: Jo <me@jguer.space>
2022-08-16 09:08:51 +02:00
transifex-integration[bot]
b882faf48a
Translate '/po/en.po' in 'fr_FR' (#1794)
Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-08-15 20:37:06 +00:00
Jo
2a2040c570
chore: use stdlib vars in code (#1793) 2022-08-14 17:41:54 +00:00
Jo
458c69e179
Update README.md 2022-08-14 16:59:09 +00:00
Jo
6ffa86908a
Bump dependencies (#1791)
* bump deps

* release as minor
2022-08-14 16:03:56 +00:00
transifex-integration[bot]
221b86bea4
Translate '/po/en.po' in 'zh_TW' (#1790)
Translate /po/en.po in zh_TW

translation completed for the source file '/po/en.po'
on the 'zh_TW' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-08-14 15:57:43 +00:00
Jo
ae6c2deda0
remove zh-Hans (#1789)
* remove zh-Hans

* remove zh-Hans from makefile
2022-08-14 15:41:22 +00:00
transifex-integration[bot]
64a26ca2bc
Translate '/po/en.po' in 'zh_CN' (#1788)
* Translate /po/en.po in zh_CN

translation completed for the source file '/po/en.po'
on the 'zh_CN' language.

* Update po/zh_CN.po

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
Co-authored-by: Jo <me@jguer.space>
2022-08-14 15:40:11 +00:00
Jo
888fb4b1d0
Fix minor question and locale issues (#1786)
* add missing locales

* use t.Setenv instead of os.Setenv for tests

* locale: present y/n if localisation is not latin. Always accept y/n in every case

* question: use operation info for question

* edge case where localised n is equal to default y

* add tests for basic locales
2022-08-13 22:56:23 +00:00
transifex-integration[bot]
85934bddea
Translate '/po/en.po' in 'zh-Hans' (#1784)
Translate /po/en.po in zh-Hans

translation completed for the source file '/po/en.po'
on the 'zh-Hans' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-08-13 20:24:16 +00:00
transifex-integration[bot]
c339a605de
Translate '/po/en.po' in 'sv' (#1785)
Translate /po/en.po in sv

translation completed for the source file '/po/en.po'
on the 'sv' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-08-13 20:23:50 +00:00
transifex-integration[bot]
0b6446f927
Translate '/po/en.po' in 'uk' (#1780)
Translate /po/en.po in uk

translation completed for the source file '/po/en.po'
on the 'uk' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-08-09 20:55:20 +00:00
Jo
e2f61255cb
Merge pull request #1779 from Jguer/jguer/migrate-provides
Config: Add migration service and migrate provides default value
2022-08-05 21:41:26 +00:00
jguer
35c91bea50
remove arm v6 support 2022-08-05 23:13:46 +02:00
jguer
c0a9b6af4f
Remake ci.Dockerfile 2022-08-05 23:05:20 +02:00
jguer
1a52da5891
Make provides disabled by default. Add Migration to set provides to disabled 2022-08-05 22:55:54 +02:00
jguer
4d5131d6c7
fixed VCS temp file remove 2022-08-05 18:56:28 +02:00
Oliver Tzeng
2214d99f8e
Created zh_TW.po (#1776) 2022-08-05 14:33:16 +00:00
transifex-integration[bot]
6f60892356
Translate '/po/en.po' in 'ru_RU' (#1745)
Translate /po/en.po in ru_RU

translation completed for the source file '/po/en.po'
on the 'ru_RU' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-07-20 14:29:06 +02:00
transifex-integration[bot]
216acead86
Translate '/po/en.po' in 'tr' (#1754)
Translate /po/en.po in tr

translation completed for the source file '/po/en.po'
on the 'tr' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-07-20 14:28:59 +02:00
transifex-integration[bot]
bda2165205
Translate '/po/en.po' in 'ko' (#1775)
Translate /po/en.po in ko

translation completed for the source file '/po/en.po'
on the 'ko' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-07-20 14:28:52 +02:00
vqrkxo
e0006ec272
feat(aur): add option to limit concurrent downloads (#1768)
* feat(aur): add option to limit concurrent downloads

Adds to config file option 'maxconcurrentdownloads' which if set to
value != 0, limits number of concurrent AUR downloads to specified.
Fixes #1763.

* fix lint issue
2022-06-28 12:05:29 +02:00
vqrkxo
18fbc24e68
fix(cmd): fix support for --defaultconfig (#1767)
This change fixes #1766.
2022-06-27 09:48:34 +02:00
Jguer
0fdfe79943
Add experimental vote util to yay (#1765)
* feat(vote): add vote utility

* update deps

* add vote and unvote capabilities

* use -W for web ops

* add fish completions

* add bash completion

* add zsh completion

* add md instructions
2022-06-17 16:55:22 +00:00
Jguer
44a0a243ed
remove gock from completion package (#1764)
* remove gock from completion package

* fix ci builder

* undo changes to default flow
2022-06-17 16:49:41 +00:00
jguer
70a9765854
bump minor 2022-06-17 10:01:53 +02:00
jguer
5439229c7d
disable noseparatesources by default 2022-06-10 18:31:21 +02:00
jguer
edef790532
update deps 2022-06-10 17:47:37 +02:00
jguer
4ca50e55f0
fix(yay): fix --yay typo. fixes #1752 2022-05-16 23:05:23 +02:00
jguer
c83b48d474
update deps 2022-05-16 23:02:34 +02:00
cubercsl
ed2f533534
docs: remove tuna aur config for China users (#1747)
Tuna has removed the aur proxy service since 01Mar:
- https://mirrors.tuna.tsinghua.edu.cn/news/remove-aur/
- tuna/issues#1424
2022-05-03 22:15:17 +02:00
jguer
74cf1f7542
update deps 2022-04-12 09:40:41 +02:00
transifex-integration[bot]
bd3e6c5236
Translate /po/en.po in id (#1742)
translation completed for the source file '/po/en.po'
on the 'id' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-04-10 16:22:56 +00:00
Drumstickx
94cff87927
Fix ContinueTask user prompt for locales with uppercase yes (#1735)
* Fix ContinueTask user prompt for locales with uppercase yes

The yes/y strings have to be converted to lowercase to enable matching of
the user input converted to lowercase. Otherwise, locales with uppercase
yes/y strings (e.g., German: `Ja/J`) will never match.

* Addess linter issue

* Remove obsolete lowercase conversion
2022-03-31 14:19:43 +02:00
transifex-integration[bot]
85023f7197
Translate /po/en.po in pt (#1732)
translation completed for the source file '/po/en.po'
on the 'pt' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-03-31 14:19:34 +02:00
transifex-integration[bot]
0f62b567ef
Translate /po/en.po in es (#1740)
translation completed for the source file '/po/en.po'
on the 'es' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-03-30 21:22:38 +00:00
transifex-integration[bot]
281ec1c926
Translate /po/en.po in de (#1739)
translation completed for the source file '/po/en.po'
on the 'de' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-03-30 21:22:11 +00:00
transifex-integration[bot]
ed62496025
Translate /po/en.po in it_IT (#1730)
translation completed for the source file '/po/en.po'
on the 'it_IT' language.
2022-03-23 10:08:08 +01:00
jguer
e773c7662c
update go-alpm 2022-03-07 17:57:20 +01:00
J Guerreiro
b8f054f988
fix(po): Update default po file (#1722)
* update default po file

* fix string
2022-03-07 10:35:20 +01:00
J Guerreiro
e4a1f018ea
feat(search): Add fuzzy name matching and mixed sources (#1719)
* fix(alpm): fix callback text

* feat(yay): Add mixed search result

* remove old result structs

* add option for controlling query builder

* only set query builder after parsing args

* add parser args

* update manpage

* write test for results

* write test for results

* mixed source test

* only sort 1 time with every mode
2022-03-06 23:48:15 +00:00
J Guerreiro
ae01f8e4a0
feat(config): Add version marker (#1720)
* only run on PRs

* prefer manual tags

* add defaults for tests

* cuddle assignment
2022-03-04 23:30:15 +00:00
J Guerreiro
dc9bef0115
fix(ci): fix builds against pacman-git (#1718)
* fix(ci): fix builds against pacman-git

* fix(ci): fix builds against pacman-git

* update dockerfile

* update dockerfile

* update dockerfile

* fix(ci): fix builds against pacman-git

* fix(ci): fix builds against pacman-git

* update go-alpm

* update alpm

* return to lopsided images

* go mod tidy
2022-03-04 23:04:55 +00:00
xvqxy
b52e3cf0d2
fix(aur): -Ss fails on multi arg if no results. (#1716)
This fixes #1674.

If search command included multiple packages and no results were found
in AUR, response from `query.queryAUR` was returned
as nil slice instead of non-nil empty slice and as nil slice is treated
during subsequent validation in `query.SourceQueryBuilder.Results` as
meaning that searching in AUR was not executed, command was failing.
Now empty non-nil slice is returned if results were not found.
2022-02-21 09:14:11 +00:00
Eng Zer Jun
83a257b16b
test: use T.TempDir to create temporary test directory (#1709)
The directory created by `T.TempDir` is automatically removed when the
test and all its subtests complete.

Reference: https://pkg.go.dev/testing#T.TempDir
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2022-02-11 09:30:54 +01:00
transifex-integration[bot]
f719a6ffca
Translate /po/en.po in tr (#1707)
translation completed for the source file '/po/en.po'
on the 'tr' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-02-10 18:25:39 +01:00
J Guerreiro
7f5a060324
feat(yay): Add support for PACMAN_AUTH (#1706) 2022-02-08 09:39:41 +01:00
transifex-integration[bot]
7f7b69447d
Translate /po/en.po in de (#1705)
translation completed for the source file '/po/en.po'
on the 'de' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-02-07 19:30:05 +00:00
transifex-integration[bot]
ba222c87f6
Translate '/po/en.po' in 'fr_FR' (#1703)
* Translate /po/en.po in fr_FR

translation completed for the source file '/po/en.po'
on the 'fr_FR' language.

* Update po/fr_FR.po

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
Co-authored-by: J Guerreiro <me@jguer.space>
2022-02-07 10:05:58 +01:00
transifex-integration[bot]
ddd8e0e1f2
Translate /po/en.po in it_IT (#1701) 2022-02-06 22:23:07 +00:00
Norbert Pfeiler
9c2d4ec0d1
feat(stats): add statistics about pacman and yay cache sizes (#1679) 2022-02-06 09:53:34 +00:00
jguer
2a3ddafce5
fix(deps): fix conflict line being printed in new line 2022-02-06 10:49:57 +01:00
jguer
0e3affd876
fix(input): fix display in wrong line of number input 2022-02-06 10:44:33 +01:00
jguer
c5bea07f64
chore(version): small patching to version 2022-02-06 10:06:31 +01:00
transifex-integration[bot]
712665e531
Translate /po/en.po in fr_FR (#1690)
at least 95% translated for the source file '/po/en.po'
on the 'fr_FR' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-02-05 13:40:17 +00:00
transifex-integration[bot]
87415a8d20
Translate /po/en.po in de (#1691)
at least 95% translated for the source file '/po/en.po'
on the 'de' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-02-05 13:40:11 +00:00
transifex-integration[bot]
58ac44c748
Translate /po/en.po in id (#1692)
at least 95% translated for the source file '/po/en.po'
on the 'id' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-02-05 13:39:51 +00:00
transifex-integration[bot]
527534af91
Translate /po/en.po in it_IT (#1693)
at least 95% translated for the source file '/po/en.po'
on the 'it_IT' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-02-05 13:39:44 +00:00
transifex-integration[bot]
8ac1da8115
Translate /po/en.po in pt (#1694)
at least 95% translated for the source file '/po/en.po'
on the 'pt' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-02-05 13:39:36 +00:00
transifex-integration[bot]
2de6d35709
Translate /po/en.po in pt_BR (#1695)
at least 95% translated for the source file '/po/en.po'
on the 'pt_BR' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-02-05 13:39:29 +00:00
transifex-integration[bot]
165433d1e3
Translate /po/en.po in es (#1696)
at least 95% translated for the source file '/po/en.po'
on the 'es' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-02-05 13:39:22 +00:00
transifex-integration[bot]
7feac514f7
Translate /po/en.po in tr (#1697)
at least 95% translated for the source file '/po/en.po'
on the 'tr' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-02-05 13:39:14 +00:00
jguer
4df76fd9fe
chore(locale): update en.po 2022-02-05 14:29:04 +01:00
J Guerreiro
d83c5ca5a1
fix(deps): update deps (#1689) 2022-02-05 14:25:59 +01:00
mh4ckt3mh4ckt1c4s
e9d8894a37
Fix(AUR): Fix end of php use on AUR API (#1685)
* Fixed end of php use on AUR API

* Added a / to the end of rpc to respect the AUR RPC doc

* Removed the / due to the "AUR unavailable at this moment" error
2022-02-05 12:56:10 +00:00
Norbert Pfeiler
19d42ae3cd
fix(yay): Total foreign → Foreign (#1680)
Rename -Ps option
2022-02-03 09:58:55 +01:00
jguer
09695c694f
remove noisy funding sources 2022-01-13 18:04:49 +01:00
jguer
65ecee9cb6 chore(deps): update ci 2022-01-03 13:46:52 +00:00
jguer
1c22cc491c chore(linting): code lint 2022-01-03 13:46:52 +00:00
jguer
d02321bdc9 chore(dep): rename pool creator 2022-01-03 13:46:52 +00:00
jguer
ac13060bfd
chore(locale): update po files 2022-01-02 21:20:11 +01:00
transifex-integration[bot]
0f5df3cbad
Chore: Translate /po/en.po in id (#1662)
translation completed for the source file '/po/en.po'
on the 'id' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2021-12-30 09:28:15 +01:00
transifex-integration[bot]
2552cf1e22
Translate /po/en.po in pt_BR (#1656)
translation completed for the source file '/po/en.po'
on the 'pt_BR' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2021-12-22 17:26:34 +01:00
Дамјан Георгиевски
f7dbebcc2c
fix(exec): add --quiet to systemd-run (#1654)
* add --quiet to systemd-run

this will avoid the `Press ^] three times within 1s to disconnect TTY.` message.

it's mentioned in the systemd-run man page too:
```
--quiet, -q
    Suppresses additional informational output while running. This is particularly useful in combination with --pty when it will suppress the
    initial message explaining how to terminate the TTY connection.
```

* add --quiet to the tests

* add --quiet to TestABSPKGBUILDRepoExistsPerms too
2021-12-20 17:49:25 +01:00
jguer
a9702da300
chore(cmd): reduce install calls 2021-12-18 21:39:01 +01:00
jguer
81eeb8c495
fix(download): use git pull --rebase --autostash instead of --ff-only. fixes #1635 2021-12-12 19:05:48 +01:00
JohnTheCoolingFan
bf288822b9 Update ru_RU.po to match pacman translation
Changed "Optional Deps" and "Provides" to match with pacman translation.
2021-12-12 17:29:27 +00:00
jguer
6c09d9bf33
chore(yay): exempt bugs and triage from stale 2021-12-10 09:06:37 +01:00
Cyrus Yip
c0507e7f8e chore(readme): improve format of FAQ 2021-12-06 18:01:18 +00:00
Thomas Dupond
7e8d61c017 Prefer VISUAL over EDITOR for editmenu 2021-12-06 07:38:01 +00:00
jguer
9ebc98e80f
chore(yay): update deps 2021-12-04 19:48:25 +01:00
transifex-integration[bot]
17c20f5cd9 Translate /po/en.po in tr
translation completed for the source file '/po/en.po'
on the 'tr' language.
2021-11-27 02:15:10 +01:00
ccn
0b2bddf9d0 Add command to install quickly
add in a command which will do all of the installation steps at once.
2021-11-27 02:14:03 +01:00
transifex-integration[bot]
df6af2e1e4 Translate /po/en.po in de
translation completed for the source file '/po/en.po'
on the 'de' language.
2021-11-21 16:27:11 +01:00
transifex-integration[bot]
40561093f1 Translate /po/en.po in fr_FR
translation completed for the source file '/po/en.po'
on the 'fr_FR' language.
2021-11-13 10:23:23 +01:00
transifex-integration[bot]
58bbcb3716 Translate /po/en.po in it_IT
translation completed for the source file '/po/en.po'
on the 'it_IT' language.
2021-11-13 10:22:51 +01:00
jguer
e38d0138a6 feat(yay): simplify sort mode and use it for calculating number menu indices, fixes #1627 2021-11-07 00:18:26 +01:00
Matt Alexander
53e8ec70f8 fix(search): Fixed unit test; make test names unique and more descriptive 2021-11-05 09:26:34 +01:00
Matt Alexander
22c165a11f feat(search): add optional setting that formats each search result on a single line, Part II 2021-11-05 09:26:34 +01:00
Matt Alexander
1c238dd724 fix(docs): Add reference to make lint in section talking about go fmt. 2021-11-05 09:26:34 +01:00
Matt Alexander
673f077e7f feat(search): add optional setting that formats each search result on a single line 2021-11-05 09:26:34 +01:00
jguer
f88bf5c212
fix(search): in case there are no results, don't display number menu 2021-11-03 00:04:00 +01:00
transifex-integration[bot]
02d33f63c0 Translate /po/en.po in de
translation completed for the source file '/po/en.po'
on the 'de' language.
2021-11-02 15:31:47 +01:00
transifex-integration[bot]
38b66fee63 Translate /po/en.po in es
translation completed for the source file '/po/en.po'
on the 'es' language.
2021-11-02 15:31:04 +01:00
jguer
15d91e4661
fix(search): revert repo query revert. Fixes #1623 2021-10-28 17:41:51 +02:00
jguer
57a9630054 chore(alpm): use text.getinput for ialpm 2021-10-27 23:45:23 +02:00
jguer
9eb73899fe chore(query): unify search in number menu selection and normal search 2021-10-27 23:45:23 +02:00
jguer
44cf54795d chore(text): use getInput for number pick deps 2021-10-27 23:45:23 +02:00
jguer
f925b719e8 chore(text): make input overflow its own error 2021-10-27 23:45:23 +02:00
jguer
005c5c24b4 chore(globals): rename config->globals for easier understanding 2021-10-27 23:45:23 +02:00
jguer
e4b1cb6e7d chore(menus): squish clean menu into diff/edit menu 2021-10-27 23:45:23 +02:00
jguer
49577191c6 chore(menus): refactor editDiff menus 2021-10-27 23:45:23 +02:00
jguer
d096d15973 feat(menus): extract menus package 2021-10-27 23:45:23 +02:00
jguer
6364b4efe8 fix(menus): improve edit and diff menu structure 2021-10-27 23:45:23 +02:00
jguer
f3c3e2e4d4 fix(clean_menu): optimize any base installed 2021-10-27 23:45:23 +02:00
jguer
a43fbacc96 fix(clean_menu): anyExistInCache already verifies if cleanMenu is necessary 2021-10-27 23:45:23 +02:00
jguer
1fb9f410a6 chore(install): reduce scope of toSkipSlice 2021-10-27 23:45:23 +02:00
jguer
34e81d5d7d fix(clean_menu): clean menu errors don't exit yay anymore 2021-10-27 23:45:23 +02:00
jguer
3fef4ae1e3 test(build-dir): add regression tests for non-existing build dir 2021-10-15 19:58:59 +02:00
jguer
12a6d4f5c1
fix(cache): create build directory if not systemd-run directory. closes #1612 2021-10-15 19:22:28 +02:00
jguer
52efaeba52
chore(yay): bump hardcoded version 2021-10-10 11:05:52 +02:00
jguer
2218e05981
fix(clean): missing dir caused failure to clean. closes #1610 2021-10-10 11:01:59 +02:00
jguer
3a6654b0c5
chore(yay): bump hardcoded version 2021-10-09 23:21:33 +02:00
jguer
8238bc9f88
chore(deps): update dependencies 2021-10-09 23:16:14 +02:00
morganamilo
1a0cfd85f2 Fix caps in search leading to no results 2021-10-05 09:06:26 +02:00
jguer
b2a728151e
test(news): remove parallel from test with race 2021-09-09 09:03:48 +02:00
jguer
4122716ea1
fix(yay): display network error on yay number menu, closes #1172 2021-09-08 23:02:49 +02:00
jguer
84e872ebe3
test(news): add test case for edge case of news in same day as build 2021-09-08 22:48:55 +02:00
jguer
555d1ead95
chore(version): bump major version 2021-09-08 22:28:08 +02:00
jguer
ba1e06d367
feat(cmd): remove unused absdir option 2021-09-08 22:25:55 +02:00
jguer
adb74b9252 chore(su): prefer short form if 2021-09-07 00:41:36 +02:00
jguer
ffb17fd2e3 feat(su): deelevate if ran as su without tool 2021-09-07 00:41:36 +02:00
jguer
bc1d900fa9 feat(su): deelevate if ran as su using a privilege elevation tool 2021-09-07 00:41:36 +02:00
Tom Boettger
251c456d70 Fix typo in readme
pacakge -> package
2021-09-03 20:51:15 +02:00
jguer
2eafbcc03e
chore(readme): update install instructions. closes #1435 2021-08-31 02:50:51 +02:00
jguer
c8fcdeae5b feat(su): use alternative privilege elevators when sudo is not available
When sudobin/custom wrapper is not available try the following in order:

- sudo
- doas
- pkexec
- su
2021-08-31 02:17:27 +02:00
Eng Zer Jun
e43c712c84 refactor: move from io/ioutil to io and os package
The io/ioutil package has been deprecated in Go 1.16. This commit
replaces the existing io/ioutil functions with their new definitions in
io and os packages.

Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2021-08-26 15:17:47 +02:00
jguer
1869bbe291
chore(install): prefer short form ifs 2021-08-23 17:37:58 +02:00
jguer
b7f9a5e677
fix(install): fix missing error display in install 2021-08-21 16:17:19 +02:00
jguer
cedbcfbcda
fix(cmd): fix -h on yay show and get. closes #856 2021-08-21 15:32:37 +02:00
jguer
a1941b24bf chore(docker): remove label 2021-08-21 15:16:28 +02:00
jguer
074798aca5 chore(docker): update golangci and squash ops 2021-08-21 15:16:28 +02:00
jguer
e14fe638c2 chore(go): update minimum go version to 1.17 2021-08-21 15:16:28 +02:00
jguer
5be99d9bf0 chore(README): small reorganization of README 2021-08-20 18:33:52 +02:00
jguer
1d903b6c7e test(sources): add PKGBUILD source tests 2021-08-17 14:26:57 +02:00
jguer
aedbcffc80 feat(sources): concurrent makepkg downloading 2021-08-17 14:26:57 +02:00
jguer
5d9e424942 chore(translations): update base po 2021-08-14 08:58:17 +02:00
transifex-integration[bot]
401922a6ed Translate /po/en.po in sv
translation completed for the source file '/po/en.po'
on the 'sv' language.
2021-08-14 08:35:16 +02:00
transifex-integration[bot]
c8c3f812f6 Translate /po/en.po in fr_FR
translation completed for the source file '/po/en.po'
on the 'fr_FR' language.
2021-08-14 08:33:47 +02:00
transifex-integration[bot]
0fccb9baaf Translate /po/en.po in es
translation completed for the source file '/po/en.po'
on the 'es' language.
2021-08-14 08:33:35 +02:00
transifex-integration[bot]
70e390f939 Translate /po/en.po in eu
translation completed for the source file '/po/en.po'
on the 'eu' language.
2021-08-14 08:33:21 +02:00
transifex-integration[bot]
a05ba32acf Translate /po/en.po in ko
translation completed for the source file '/po/en.po'
on the 'ko' language.
2021-08-14 01:00:02 +02:00
transifex-integration[bot]
d007a54e3a Translate /po/en.po in ru_RU
translation completed for the source file '/po/en.po'
on the 'ru_RU' language.
2021-08-14 00:59:46 +02:00
transifex-integration[bot]
0db92c990f Translate /po/en.po in pt_BR
translation completed for the source file '/po/en.po'
on the 'pt_BR' language.
2021-08-14 00:59:13 +02:00
transifex-integration[bot]
87dc57323b Translate /po/en.po in ja
translation completed for the source file '/po/en.po'
on the 'ja' language.
2021-08-14 00:58:45 +02:00
jguer
e231b2b02d chore(yay): modernize exec commands 2021-08-14 00:10:43 +02:00
jguer
3de02dbd73 fix(install): -debug sufix is optional 2021-08-11 22:28:24 +02:00
jguer
49ef780602 chore(makefile): add atomic cover back to go test 2021-08-11 22:19:55 +02:00
jguer
95bc333a92 test(yay): port tests to parallel 2021-08-11 22:19:55 +02:00
jguer
cb4b57f6d8 chore(golangci): add new linters 2021-08-11 22:19:55 +02:00
jguer
08d1305ec5 feat(download): respect aur url settings and update deps 2021-08-11 22:19:55 +02:00
jguer
f42cbde6db refactor(parser): unify atleast modes 2021-08-09 13:32:55 +02:00
jguer
5c11c01d85 refactor(exe): move sudo loop into CmdBuilder 2021-08-08 21:02:35 +02:00
jguer
444151de0d refactor(vcs): implement GitBuilder interface 2021-08-08 17:45:07 +02:00
jguer
fc20bde10d chore(prettier): apply prettier on files 2021-08-08 17:45:07 +02:00
jguer
172ee1f4e8 refactor(exe): port pacman calls to cmd_builder 2021-08-08 17:45:07 +02:00
jguer
1a2e5b9529 refactor(settings): move args to own package to allow pacman cmd builder 2021-08-08 17:45:07 +02:00
jguer
456d83ef51 fix(download): cloned reflects if package was pulled or cloned 2021-08-07 19:17:58 +02:00
jguer
186c7a54fe fix(dep): ensure assume-installed works with depstrings 2021-08-07 19:17:58 +02:00
Jonas Bögle
7dda0fa2f8 fix: skip --assume-installed packages 2021-08-07 19:17:58 +02:00
jguer
1c96fd2d9d refactor(diff): move diff functions to dedicated file 2021-08-07 19:17:58 +02:00
jguer
522247dca2 feat(install): use download package for PKGBUILD repo download 2021-08-07 19:17:58 +02:00
jguer
4a515c4462 test(downloads): remove non reproducible tests 2021-08-05 10:11:54 +02:00
jguer
ab902e9cc6 chore(vcs): port VCS to download package 2021-08-05 10:11:54 +02:00
jguer
b8641256ab chore(download): rename PKGBUILD function to match package 2021-08-05 10:11:54 +02:00
jguer
65b1c4be69 refactor(main): move get cmds to their own file 2021-08-05 10:11:54 +02:00
jguer
2ed099a263 chore(download): unify filtering logic between commands 2021-08-05 10:11:54 +02:00
jguer
98378642bc test(download): add tests for repo downloading 2021-08-05 10:11:54 +02:00
jguer
413ad23abc chore(download): rename public functions 2021-08-05 10:11:54 +02:00
jguer
79b44fd544 feat(download): download PKGBUILD repos interface 2021-08-05 10:11:54 +02:00
jguer
50c0ece9a7 chore(style): run gofumpt on packages 2021-08-05 10:11:54 +02:00
Peter Wu
827adab4b8 Update yay.8 2021-07-31 16:47:16 +02:00
jguer
99c3c98264
fix(docker): fix dockerfile formatter removing breakline 2021-07-29 22:22:32 +02:00
jguer
20d5fd406f fix(download): fix -Gp for repository packages 2021-07-26 09:07:27 +02:00
jguer
d460e7ae5b
chore(ci): update golangci, delete old test_script 2021-07-25 12:19:59 +02:00
jguer
e4a933da52 fix(upgrade): add special devel fix for vscode 2021-07-25 11:53:14 +02:00
Oliver Mangold
4a9319920b fixing URLs for ABS after upstream change from git.archlinux.org to github 2021-07-13 18:39:18 +02:00
jguer
61e38ad2fc chore(pacman): remove pacman 5 support 2021-07-03 18:27:14 +02:00
OneOfOne
c9e63fff0d Makefile: GOFLAGS-> FLAGS since it's a reserved kw 2021-07-03 18:02:56 +02:00
jguer
169d5f1f62 fix(vcs): make command error more explicit 2021-07-03 17:15:36 +02:00
jguer
d6cb4c50e8 fix(locale): add ko to makefile LANGs 2021-06-27 11:54:14 +02:00
Adenosie
2ac2374b47 Add Korean translation 2021-06-13 20:10:34 +02:00
jguer
040c8fe380 fix(aur): fix lint issue 2021-06-09 03:57:37 +02:00
jguer
8fd4f78400 chore(aur): update to latest version 2021-06-09 03:57:37 +02:00
jguer
93bb2867be chore(http): use client for yay -Sl 2021-06-09 03:57:37 +02:00
jguer
a37f4efd73 chore(http): use client for pkg completions 2021-06-09 03:57:37 +02:00
jguer
c7cf7baa48 chore(http): use client for news feed 2021-06-09 03:57:37 +02:00
jguer
e20ab8b6a2 fix(aur): fix wrong string usage 2021-06-09 03:57:37 +02:00
jguer
545ea772eb feat(aur): customize User Agent 2021-06-09 03:57:37 +02:00
jguer
6e70e2a59d
chore(update): bump minor 2021-06-03 22:02:23 +02:00
jguer
c922cb3509
chore(update): tentative fix of docker oom 2021-06-03 21:46:41 +02:00
jguer
059ca9d9b8
chore(update): use ubuntu-latest for builder image 2021-06-01 23:36:50 +02:00
jguer
418b578e9e
chore(upgrade): add pacman to install list
Revert "chore(update): fix line break"

This reverts commit a24fb50e6d6f17b57d99e1c0e2d1c3421f24cb32.

Revert "chore(update): update golangci and force rebuild"

This reverts commit 25efafe25e18e4ddd85be51b866ced7481b5fcb0.
2021-06-01 23:09:17 +02:00
jguer
a24fb50e6d
chore(update): fix line break 2021-06-01 21:19:50 +02:00
jguer
25efafe25e
chore(update): update golangci and force rebuild 2021-06-01 08:34:16 +02:00
x1b6e6
ba3c66949a fix(sync) Don't asks for provider twice 2021-05-26 13:51:17 +02:00
jguer
4993e4dfbd
chore(makefile): tag smaller release 2021-05-10 08:53:12 +02:00
jguer
c153631064
ci(release): tweak release 2021-05-08 00:38:32 +02:00
jguer
b0d87b7a09
chore(makefile): change default patch 2021-05-08 00:36:03 +02:00
jguer
ab0ce0aeb9
chore(deps): update dependencies 2021-05-08 00:31:19 +02:00
jguer
8e6d098ff8
fix(deps): fix regression on CombinedDepList 2021-05-08 00:29:33 +02:00
x1b6e6
662c630a08 main_test: diaable checking TotalDownload conf
`TotalDownload` is removed in pacman 6. We can't leave it enabled for
pacman 5 and disable for pacman 6, so we disable it for both.
2021-05-07 23:30:08 +02:00
x1b6e6
faf03436a7 Sync with go-alpm v2.0.5 2021-05-07 23:30:08 +02:00
x1b6e6
7ade2afd78 add testing with pacman-git 2021-05-07 18:03:57 +02:00
x1b6e6
767c7975f3 fix test using multiple 'Architecture' value
Signed-off-by: x1b6e6 <ftdabcde@gmail.com>
2021-05-04 08:43:56 +02:00
x1b6e6
de113b87f3 Add support for multiple 'Architecture' values
This allows architecture to be multivalued. On x86-64 machines, this
could be something like:
    Architecture = x86-64-v3 x86-64

We use the first specified Architecture value in mirrorlist $arch
variable replacement, as this is backwards-compatible and sane.

More info:
https://git.archlinux.org/pacman.git/commit/?id=3179db108a83104d9de6d1d607f55f8118e92160

Signed-off-by: x1b6e6 <ftdabcde@gmail.com>
2021-05-04 08:43:56 +02:00
morganamilo
29f4c43227 Sort upslice by pacman repo order 2021-04-24 17:28:53 +02:00
morganamilo
f04a469324 Make UpSlice a struct instead of a type alias 2021-04-24 17:28:53 +02:00
Ferdinand Bachmann
64e5b9e93d fix(query): allocate capacity in filterDebugPkgs
(addresses review in PR #1484 by Jguer)
2021-04-20 22:01:46 +02:00
Ferdinand Bachmann
3efb43af95 feat(query): list debug packages missing from AUR separately 2021-04-20 22:01:46 +02:00
Porrumentzio
5184ef6a04 Update eu.po 2021-04-20 22:00:30 +02:00
Porrumentzio
f3dd3467ec Update eu.po 2021-04-20 22:00:30 +02:00
Porrumentzio
93d3c87624 Update eu.po
```
135    #: print.go:281
136    msgid "Check Deps"
```
_Dependencies that make checking_, _Currently checking dependencies_ or _The process of checking dependencies_?

```
299    #: print.go:298
300    msgid "Package Base ID"
301    msgstr "Pakete Base IDa"
302    
303    #: print.go:299
304    msgid "Package Base"
305    msgstr "Pakete Base-a"
```
Is _Package Base_ a proper noun or simpli _The base of the package_?

```
363    #: print.go:300
364    msgid "Snapshot URL"
365    msgstr "Snapshot-aren URLa"
```
What does mean Snapshot here? Is it an "image" of the repo or package, meaning that maintains that version?
2021-04-20 22:00:30 +02:00
jguer
85fe90c95f
ci(yay): tweak release notary 2021-04-12 17:21:29 +02:00
jguer
d3bc19a003 fix(yay): fix proxy settings 2021-04-12 16:54:51 +02:00
jguer
ca7165ecb6 ci(yay): update golangci and remove deprecated 2021-04-12 16:54:51 +02:00
jguer
2da1e154c2
fix(yay): strip release bin 2021-03-20 18:59:46 +01:00
jguer
6d4c2472d8 fix(yay): use upstream flags 2021-03-20 18:41:29 +01:00
jguer
829f944659
fix(output): don't print exit status message 2021-03-18 22:40:37 +01:00
jguer
d1a6ead81c style(deps): reduce scope where possible 2021-03-17 23:18:44 +01:00
jguer
a33f43f502 feat(deps): don't install check deps if --nocheck is passed.
feat(makepkg): automatically pass -d to makepkg if -dd is passed
2021-03-17 23:18:44 +01:00
jguer
d7424b1b28 feat(install): implement support for -dd 2021-03-17 23:18:44 +01:00
jguer
5da8161328 chore(install): reduce cognitive load 2021-03-17 23:18:44 +01:00
jguer
7a280e0199 fix(install): simplify sysupgrade 2021-03-17 23:18:44 +01:00
jguer
ece81fa078
chore(yay): bump for next version and add funding options 2021-03-16 19:26:23 +01:00
J Guerreiro
5f2a14d027
add asciicast 2021-03-15 23:54:30 +01:00
jguer
3d585877ba ci(yay): update golangci 2021-03-13 12:24:01 +01:00
jguer
bfd5ca6024
ci(github): tame stale bot 2021-03-13 12:21:15 +01:00
jguer
4b476a0e78
fix(sudoloop): reduce sudoloop sleep, fixes #1379 2021-03-13 12:08:11 +01:00
jguer
b7f730a59a
fix(sudoloop): stop update sudo from taking extra flags, fixes #1427 2021-03-13 12:06:31 +01:00
Julian Daube
348f2de85c fix(yay): mirror pacman return codes on run error
makepkg expects pacman-alikes to return 127 (missing dependency)
when called with -T as argument. yay does not behave like this since
it overwrite the return value with 1 in every case.

This commit will test for ExitCode errors and return the called
applications error code instead of 1, when possible. This gets yay
closer to be used as pacman alternative for makepkg with

```
PACMAN=yay makepkg -s
```

fixes  Jguer/yay#1455
2021-03-12 20:42:42 +01:00
D1CED
c8a74cb4a4 prevent leaking goroutines 2021-03-12 20:41:55 +01:00
D1CED
260594f42a disable interfacer check 2021-03-12 18:11:30 +01:00
D1CED
eb9ff86aa9 fixed lint issues 2021-03-12 18:11:30 +01:00
Jannis M. Hoffmann
1a8ee2012d removed alpm dependency from download 2021-03-12 18:11:30 +01:00
Jannis M. Hoffmann
df1361aa05 moved Upgrade to db
to reduce dependencies
2021-03-12 18:11:30 +01:00
Jannis M. Hoffmann
5d2c763fdb reroute dep->rpc dependency via query 2021-03-12 18:11:30 +01:00
Jannis M. Hoffmann
34f7dbc7fc Narrow interface in completion 2021-03-12 18:11:30 +01:00
Jannis M. Hoffmann
5993613c53 Added type alias for alpm types in db 2021-03-12 18:11:30 +01:00
jguer
b6feb7c308
chore(deps): update dependencies 2021-03-11 09:36:42 +01:00
jguer
ca60c15911
chore(deps): update pre-commit hooks and fix supply chain issue 2021-03-02 08:23:26 +01:00
xvqxy
05c2fa6087 Add doc for getFilter func. 2021-02-14 22:38:49 +01:00
xvqxy
9fc6d9c128 fix(query): Respect --deps and --explicit.
This fixes #1283.

yay --query --upgrades [--explicit|--deps] will not return same packages pacman
does extended with AUR packages.
yay --show --numberupgrades [--explicit|--deps] will also work.
2021-02-14 22:38:49 +01:00
jguer
fd9e2638f9
fix(yay): fix completions of -Gp 2021-01-31 19:07:08 +01:00
jguer
afc72daba7
test(download): test pkgbuild fetching 2021-01-31 19:07:08 +01:00
jguer
ee3c51f6f2
feat(pkgbuild): extract logic 2021-01-31 19:07:08 +01:00
jguer
28c985b378
fix(pkgbuild): move pkgbuild to Gp 2021-01-31 19:07:08 +01:00
jguer
a288dd754f
fix(pkgbuild): rebase pkgbuild fetching from nycex 2021-01-31 19:07:08 +01:00
nycex
d9b440c929
implement yay -Pp option
fix linting errors

format with make fmt

fix completions

resolve most of the requested changes

move loop over dbSlice back into makeRequest

query aur in chunks of 20

check for status codes

continue instead of return in for loop

format code
2021-01-31 19:07:08 +01:00
Benjamin Robin
5bef39b728 Do not use CmdRunner.Capture() while executing a command with sudo
Fix #1440
2021-01-31 18:01:50 +01:00
jguer
b9d3992f13
chore(yay): update release notary 2021-01-29 22:48:05 +01:00
jguer
c221d5ebbd
fix(yay): give priority to LC_MESSAGES. fixes #1442 2021-01-29 22:45:42 +01:00
jguer
f00630fd0c
chore(yay): update go dependencies 2021-01-29 22:23:36 +01:00
xhqr
24edabe5df Print error if PATH is empty.
This fixes #881.

Before:
```bash
$ PATH= /usr/bin/yay -Qu
$ echo $?
1
```

After:
```bash
$ PATH= ./yay -Qu
exec: "pacman-conf": executable file not found in $PATH
$ echo $?
1
```
2021-01-16 18:27:39 +01:00
Porrumentzio
607d28778e Updated Basque translation 2021-01-13 11:42:12 +01:00
riiga
5f9ad882f6 Add Swedish translation 2021-01-11 22:21:41 +01:00
jguer
7d849a8dec
ci(release): tweak changelog 2020-12-14 19:21:15 +01:00
jguer
f7d6ad59cd
fix(completions): list installed is not available upstream yet 2020-12-11 10:24:32 +01:00
jguer
b864d433df
fix(completions): sync completions with upstream, namely packages 2020-12-11 01:30:58 +01:00
ccat3z
bb50b4079f fix(exe): fix timeout of OSRunner.Capture()
kill all child processes
2020-12-10 09:07:33 +01:00
jguer
182f4c7f6c
test(pgp): temporary fix for random order test 2020-12-10 00:29:56 +01:00
jguer
c85bbf54da
chore(yay): remove deprecated dependency 2020-12-10 00:18:23 +01:00
jguer
999392640e
fix(makefile): add pacman>6 checking to makefile 2020-12-10 00:15:00 +01:00
jguer
35e64deca4
chore(yay): update alpm 2020-12-10 00:06:46 +01:00
morganamilo
952e801ed1 Update email 2020-12-06 15:16:31 +01:00
jguer
f24e21105e
ci(builder): update ci builder 2020-11-17 20:25:40 +01:00
jguer
8d9fed2ad0
fix(query): fix remote package slow filtering 2020-11-16 08:59:35 +01:00
Kazuki Sawada
8c4fe837d3 Fill fields of CmdBuilder with options specified in commandline 2020-11-15 15:13:44 +01:00
Janek
13caa7fb3d Improve feature request issue template 2020-11-15 15:10:58 +01:00
Janek
8393ee4acc Improve bug report issue template 2020-11-15 15:10:58 +01:00
jguer
de7373dd00
docs(stale): add stale checks 2020-11-01 09:49:07 +01:00
jguer
981c336541
docs(github): add CODEOWNERS and other aur helpers section 2020-11-01 09:35:50 +01:00
jguer
6beefb1400
fix(config): fix save config 2020-11-01 09:09:38 +01:00
lwlf
497f94ca9f
parent 55a6767b44a4aa8093dff6ff30058b985939a4e0
author lwlf <wuhaojie_2011@sina.com> 1603706823 +0800
committer jguer <me@jguer.space> 1604217613 +0100
gpgsig -----BEGIN PGP SIGNATURE-----

 iQIzBAABCAAdFiEEP4pAozwhlAIAKURxbWzJvqhVazUFAl+eaw0ACgkQbWzJvqhV
 azU8Dg//XEcgshJ3DqS54kGDOhOXvp3RGWDW6EGYS9UG22ul6af2Yxgt+sNksQY2
 oaTBV2TiQ979AWikJ70waTLQEkkqqkbG1eKz0AWniQQuHPmpTu/9CA6e5hX5f+nh
 AdeUdvz8UcxRd2n80YCVYv7KEs2QkR1ZU4gE+42BG/ohdWSoAxfd1S/skDJ2uYjx
 VUu/ZVYh1qsgLhoIqLpnKzv0jPDVvbpzQ0il+1Zyl2P7qC8QKDTHkfajO6FQV46t
 74anbsk1HrwJSK2G8Z25h/vzi+6Vz36koS7A8mQCLz4jpQZho0Ax7wkhPwLhYSNf
 8CRTK8uoyczTLECOBXnwwrQoDl21N4EgqzZWDBso6DEozBZMteSd74liye3FAXff
 vWmN12pf6nh8uUN5kUGaj/Jq6XWij0cdU4vM0CpRzlzAw3OV4Z0RTwVHqAy6qU6J
 M73cIwVjzVEmPAjWlD2y1ZbTNFUsbWNZ4iqxWXcvc2IOuR4Ywjv+T45NcFXB6RcZ
 PoEivscWy/nEkmihtn9bAIhZXze/UMgIktOV8FyrYFizlVNmZ3HuFPlu6tXRFIZK
 RdGbTeLunPGMLg8iw5C9rKe8/vvsVlUpLUiguyr7w3vUS9QpxLPd8mHlsUETp3p2
 OKxz3zD4/BRaRmuL6Gef7KeroCvJYZGnVpTHPHoOENkSzGxqHCk=
 =D+N6
 -----END PGP SIGNATURE-----

unsaved config when yay's config path does not exist

unsaved config when yay's config path does not exist

unsaved config when yay's config path does not exist

unsaved config when yay's config path does not exist

unsaved config when yay's config path does not exist

fix: unsaved config when yay's config path does not exist

fix: unsaved config when yay's config path does not exist

fix: unsaved config when yay's config path does not exist

fix: unsaved config when yay's config path does not exist

fix: unsaved config when yay's config path does not exist
2020-11-01 09:00:47 +01:00
jguer
55a6767b44 fix(parser): use Fields instead of Split 2020-10-30 11:09:06 +01:00
jguer
81b5feb68a fix(passer): fix slice copying for passer 2020-10-30 11:09:06 +01:00
jguer
7d080df6ac fix(config): fix mflags and gitflags 2020-10-26 09:32:22 +01:00
jguer
c4dc9bec6e fix(ialpm): fix typo 2020-10-26 09:32:22 +01:00
jguer
01e6d80525
fix(release): fix release pipeline 2020-10-18 20:59:37 +02:00
jguer
1c779da3ed
ci(version): version bump 2020-10-18 20:31:46 +02:00
jguer
39717f3974
ci(release): automatic changelog 2020-10-16 13:12:22 +02:00
jguer
f6cb0bc460
fix(provide): fix dep pool populate with unversioned deps 2020-10-06 13:40:19 +02:00
jguer
7979279c3f
fix(provide): fix provides not finding pagure-database=5.1.0 2020-10-06 06:25:18 +02:00
jguer
678d10e04e
fix(upgrade): export upgrades 2020-10-01 14:06:21 +02:00
jguer
e1b632cf3d
build(deps): update dependencies 2020-10-01 13:39:48 +02:00
jguer
3e698f313a
test(alpm): implement alpm interfaces 2020-10-01 13:38:03 +02:00
jguer
a80771c37e
ci(dockerfile): clean pkg cache 2020-09-29 10:32:29 +02:00
jguer
1f3020e7d6
ci(dockerfile): reduce image size and update readme 2020-09-29 09:38:56 +02:00
jguer
d6aa288ab8
fix(alpm): update alpm version 2020-09-20 17:00:45 +02:00
jguer
8a0a0cf2bc
fix(ci): fix CI issues 2020-09-08 00:02:28 +02:00
x-usr
5ae510ad6f fix(display): adapt padding to match pacman's.
This change fixes part of #1332 related to splitting information into
multiple lines.
Change is implemented based on current pacman's code.
2020-09-07 23:39:19 +02:00
jguer
5c05811ae5
fix(config): fix unsaved config 2020-09-03 18:26:57 +02:00
jguer
3b2bb28e81
feat(exe): AddMakepkg builder 2020-08-22 01:24:52 +02:00
jguer
1db50882e9
feat(vcs): refactor and test cover 2020-08-22 00:39:26 +02:00
jguer
842dfb28a4
fix(ci): update dependencies 2020-08-21 02:49:09 +02:00
jguer
72f120b0f3
feat(config): refactor config to allow running without config 2020-08-21 02:39:52 +02:00
jguer
c5af6f8189
feat(vcs): package vcs 2020-08-21 02:37:03 +02:00
jguer
c1171d4146
style(runner): use command runner for commands 2020-08-19 01:48:07 +02:00
jguer
ab3c66ab98
fix(clean): Add clean directory warning on fail 2020-08-19 00:59:18 +02:00
jguer
ce79d53eb2
fix(deps): fix wrong method implementation 2020-08-19 00:42:59 +02:00
jguer
7b70462a7c
fix(noconfirm): move noconfirm to config global 2020-08-19 00:30:41 +02:00
jguer
9ad2862b7d
feat(vcs): test upDevel and extract OS runner 2020-08-19 00:30:41 +02:00
jguer
4a761c287b
test(upgrade): test AUR upgrades 2020-08-19 00:30:34 +02:00
jguer
c305e1c196
fix(color): remove duplicate colors 2020-08-17 00:09:43 +02:00
jguer
1ccfd3f0b7
fix(dbExecutor): rename functions to use dbExecutor 2020-08-17 00:02:14 +02:00
jguer
7a3794ae75
feat(interface): interface dbexecutor 2020-08-16 23:42:00 +02:00
jguer
388aaf6d7f
fix(tests): test handle creation 2020-08-16 23:42:00 +02:00
Jguer
f3925e2481
fix(sync_list): remove alpmHandle
wip
2020-08-16 23:41:56 +02:00
Jguer
6b973c6bc5
fix(pkgbuild_download): remove alpmHandle 2020-08-16 23:41:56 +02:00
Jguer
0e2a02b512
fix(clean): extract alpmHandle 2020-08-16 23:41:56 +02:00
Jguer
7bcf2ecb4c
fix(statistics): use alpm executor 2020-08-16 23:41:55 +02:00
jguer
94f650f4d3
style(main): remove alpmHandle 2020-08-16 23:41:55 +02:00
jguer
005b29373d
fix(install): remove alpmHandle 2020-08-16 23:41:55 +02:00
jguer
9fd6917bc0
fix(alpm): transfer handle initialization to executor 2020-08-16 23:41:55 +02:00
jguer
8165174462
fix(dbExecutor): move dbExecutor to runtime config 2020-08-16 23:41:55 +02:00
jguer
ad9bc9ef8f
feat(db): abstract db ops need for dep 2020-08-16 23:41:55 +02:00
hayao
7f4c277ce7 fix(translate) : Japanese 2020-08-16 12:40:31 +01:00
Adam G. Emerson
5647aab025
Updated Readme to include note about dependencies (#1345)
* Updated Readme to include note about dependencies

Per issue #1142 I've added a note to the readme to let users know they'll need to install `base-devel`.

* Update README.md

* Update README.md

Clarified note regarding base-devel.
2020-08-12 19:53:45 +01:00
Feresey
0adda5da4f Fix issues 2020-08-08 17:00:18 +01:00
jguer
8074179112
style(filter): make filtering names more relevant 2020-07-27 01:24:28 +02:00
jguer
520dd58999
style(cmd): remove useless filtering 2020-07-27 00:57:52 +02:00
jguer
37372c883f
test(news): add tests for news package 2020-07-27 00:46:32 +02:00
jguer
2cc72685ad
docs(contributing): remove outadated info 2020-07-27 00:17:05 +02:00
jguer
5b7cee1422
style(news): clarify variable 2020-07-27 00:11:24 +02:00
jguer
991c5b4146
style(parser): simplify ignore option adding 2020-07-26 23:48:33 +02:00
jguer
de92f24ce5
style(parser): simplify option creation 2020-07-26 23:40:21 +02:00
jguer
c159c06222
fix(parser): fix alpm initialize 2020-07-26 23:08:47 +02:00
jguer
7dd5d7e13e
fix(parser): fix ignores getting... ignored 2020-07-26 23:00:28 +02:00
jguer
cd8fb7143c
test(completion): add basic AUR tests 2020-07-25 00:55:46 +02:00
jguer
fa2af37eab
fix(pre-commit): add commitizen 2020-07-24 23:45:56 +02:00
jguer
45d7da4df6
fix(tests): move runes test to text package 2020-07-24 23:24:53 +02:00
jguer
1177493895
fix(vcs): remove custom testing function 2020-07-23 13:53:59 +02:00
Fifis
b37a1f7ab4 Update ru_RU.po
Added a missing word
2020-07-22 09:34:45 +02:00
Fifis
4ebbe723bd Update ru_RU.po
Re-submitting revised edits after a review.
2020-07-22 09:34:45 +02:00
Fifis
2aee260a9b Update ru_RU.po
Many unclear points and ambiguous translations improved since some of the terms were quite vague, with commentaries justifying the edits.
2020-07-22 09:34:45 +02:00
Jan Hagelauer
22acf2ad8f fix: adjust translations (fix #1329) 2020-07-20 13:46:20 +02:00
jguer
17eb6b6d10
fix(providers): fix provider display 2020-07-20 09:58:14 +02:00
jguer
a8ba2a808b
fix(docker): fix dockerignore 2020-07-20 09:27:25 +02:00
kusakata
f91489a2ee Add Japanese translation 2020-07-15 09:27:10 +02:00
jguer
fd7934d9ee
style(pretty): pretty files 2020-07-11 00:52:02 +02:00
jguer
42f337f2a5
fix(pgp): package key importing 2020-07-11 00:48:30 +02:00
jguer
7ed2cf7b49
fix(download): fixes #1322 2020-07-10 03:02:34 +02:00
jguer
054a9ac613
refactor(dep): package dep 2020-07-10 02:36:45 +02:00
jguer
e47c9584c1
fix(format): gofumt files 2020-07-08 03:40:50 +02:00
jguer
a0cff2b622
fix(cmdArgs): reorder args 2020-07-08 03:31:35 +02:00
jguer
7a32ffdd37
fix(cmdArgs): deglobal cmd args 2020-07-08 03:22:01 +02:00
jguer
93719d488c
fix(deps): update parser method 2020-07-07 01:46:39 +02:00
Jonas Bögle
71929cb225
ignore dependency checks on double --nodeps 2020-07-07 01:43:22 +02:00
J Guerreiro
0c8ce4f033 Update issue templates 2020-07-06 03:53:20 +02:00
jguer
0eb8c25c97
fix(workflow): move contributing 2020-07-06 03:52:09 +02:00
jguer
7b3de13e65
fix(workflow): remove outdated ISSUE_TEMPLATE 2020-07-06 03:51:50 +02:00
J Guerreiro
2b759fcecd Update issue templates 2020-07-06 03:50:29 +02:00
jguer
1cbef97ae4
fix(parser): fix tests on list elements 2020-07-06 02:49:33 +02:00
jguer
b1a6d154c3
fix(parser): add tests and rename method handles 2020-07-06 02:34:28 +02:00
jguer
2f88858ce6
fix(parser): squash globals into options 2020-07-06 02:02:12 +02:00
jguer
d507e024bd
fix(parser): add support for multiple overlapping global options 2020-07-06 01:16:51 +02:00
jguer
56760e1b7c
fix(parser): add support for stackable parameters. Fixes #1155, Fixes #1280 2020-07-06 00:28:04 +02:00
jguer
5cc47503be
fix(tests): use testify for tests 2020-07-05 18:03:22 +02:00
jguer
b5e70ae84b
fix(config): remove commented code 2020-07-05 17:46:17 +02:00
jguer
36730a41e3
fix(config): package alpmHandle into runtimeSettings 2020-07-05 16:58:35 +02:00
jguer
968b61ac57
fix(config): pack pacmanConf into runtime 2020-07-05 15:34:27 +02:00
jguer
488d9537db
fix(download): fixes #1228 and -Gf 2020-07-05 14:06:31 +02:00
jguer
9caecd9aee
fix(depPool): fixes #1177 with smaller query sizes 2020-07-05 13:39:17 +02:00
jguer
2dd9395190
fix(depPool): fixes #1177, add versioned dependency to AUR query 2020-07-05 13:30:28 +02:00
jguer
5a77dd86e1
fix(po): update locale template 2020-07-05 09:36:27 +02:00
jguer
0bae6e2f7d
fix(config): move default settings to config 2020-07-05 09:36:27 +02:00
jguer
9b49f76bbd
fix(config): vcs filepath fix 2020-07-05 09:36:27 +02:00
jguer
2dc01d8a3e
fix(init): refactor init to use runtime 2020-07-05 09:36:27 +02:00
jguer
cd02fa459c
fix(alpm): rename tests 2020-07-05 09:36:27 +02:00
jguer
fb5b6b74fe
fix(parser): package parser 2020-07-05 09:36:27 +02:00
jguer
cb8a988701
fix(news): package news and begin settings 2020-07-05 09:36:27 +02:00
Giftpflanze
fff9d74764 Fix "%s is up to date -- skipping" message formatting 2020-07-05 09:35:52 +02:00
jguer
7efeaaff81
fix(makefile): remove vendor path 2020-07-04 23:06:06 +02:00
jguer
feb2cea9e0
fix(ci): force update 2020-07-04 23:04:48 +02:00
jguer
6bfcfc168f
fix(docs): update badges 2020-07-04 21:31:51 +02:00
jguer
b5b4484c5c
fix(cleanafter): do not remove built packages on cleanafter 2020-07-01 09:14:54 +02:00
Matheus Alcantara
3549aeca26 Fix #1279: Error not git repository on yay -Sc 2020-06-30 09:56:31 +02:00
223 changed files with 42722 additions and 12140 deletions

26
.devcontainer/Dockerfile Normal file
View File

@ -0,0 +1,26 @@
# Use the jguer/yay-builder image as a parent image with archlinux
FROM docker.io/jguer/yay-builder
# Install extra packages (pacman-contrib and fish)
RUN sudo pacman -Syu --noconfirm pacman-contrib fish git-delta openssh bat go
# Set passwordless sudo for the docker user
RUN echo "docker ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/docker
# Create a non-root user and switch to it
USER docker
# Install xgotext
RUN go install github.com/leonelquinteros/gotext/cli/xgotext@latest
# Add /app/bin to the PATH
ENV PATH="/app/bin:$PATH"
# add /home/docker/go/bin to the PATH
ENV PATH="/home/docker/go/bin:$PATH"
# Set the working directory
WORKDIR /workspace
# Command to run when starting the container
CMD ["bash"]

View File

@ -0,0 +1,14 @@
{
"name": "Existing Dockerfile",
"build": {
"context": "..",
"dockerfile": "../.devcontainer/Dockerfile"
},
"customizations": {
"vscode": {
"extensions": [
"golang.go"
]
}
}
}

9
.dockerignore Normal file
View File

@ -0,0 +1,9 @@
*
!*.go
!pkg
!go.mod
!go.sum
!Makefile
!po
!doc
!completions

1
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1 @@
* @Jguer

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
github: [Jguer]

View File

@ -1,21 +0,0 @@
#### Affected Version
<!-- Please ensure you are using the latest yay-git package -->
<!-- Use `yay -V` to get installed version -->
<!-- Example: `yay v8.1139.r0.g9ac4ab6 - libalpm v11.0.1` -->
#### Issue
<!-- The following sections may be left out if not relevant -->
#### Steps to reproduce
<!-- Use exact commands where applicable -->
1.
2.
3.
#### Output
<!-- Include the FULL output -->
<!-- Include any relevant commands/configs -->
<!-- The current yay config can be printed with `yay -Pg` -->
<!-- Use code blocks -->
<!-- Paste services are only needed for excessive output (>500 lines) -->

44
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,44 @@
---
name: Bug report
about: Report a malfunction to help us improve
title: ""
labels: "Status: Triage, Type: Bug"
assignees: ""
---
### Affected Version
<!-- Please ensure you are using the latest yay-git package
Use `yay -V` to get installed version
Example: `yay v8.1139.r0.g9ac4ab6 - libalpm v11.0.1` -->
### Describe the bug
<!-- A clear and concise description of the bug. -->
### Reproduction Steps
1.
2.
3.
### Expected behavior
<!-- A clear and concise description of what you expected to happen. -->
### Output
<!--
Include the FULL output of any relevant commands/configs
The current yay config can be printed with `yay -Pg`
Paste services are only needed for excessive output (>500 lines)
Use --debug to add pacman and yay debug logs
or add the following key to your ~/.config/yay/config.json to only get yay debug logs
{
"debug": true
}
-->
```sh
```

View File

@ -0,0 +1,23 @@
---
name: Feature request
about: Suggest an idea for this project
title: ""
labels: "Type: Feature Request, Status: Discussion Open"
assignees: ""
---
### Is your feature request related to a problem? Please describe.
<!-- A clear and concise description of the problem, e.g. I'm always frustrated when ... -->
### Describe the solution you'd like
<!-- A clear and concise description of what you want to happen. -->
### Describe alternatives you've considered
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
### Additional context
<!-- Add any other context or screenshots about the feature request here. -->

15
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,15 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "gomod" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
groups:
go-all:
patterns:
- '*'

20
.github/stale.yml vendored Normal file
View File

@ -0,0 +1,20 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 120
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 40
# Issues with these labels will never be considered stale
exemptLabels:
- "Status: In Progress"
- "Status: Confirmed"
- "Status: Approved"
- "Status: Triage"
- "Type: Bug"
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

143
.github/workflows/builder-image.yml vendored Normal file
View File

@ -0,0 +1,143 @@
name: Builder Image
on:
schedule:
- cron: "0 3 * * 1" # Every Monday at 3 AM
push:
paths:
- "ci.Dockerfile"
- ".github/workflows/builder-image.yml"
env:
REGISTRY_IMAGE: jguer/yay-builder
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm/v7
- linux/arm64
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY_IMAGE }}
ghcr.io/${{ env.REGISTRY_IMAGE }}
tags: |
type=raw,value=latest
type=sha,format=long
- name: Build and push by digest
id: build
uses: docker/build-push-action@v5
with:
context: .
file: ci.Dockerfile
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
echo -n "$digest" > "/tmp/digests/$(echo "${{ matrix.platform }}" | tr '/' '_')"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digest-${{ matrix.platform == 'linux/amd64' && 'amd64' || matrix.platform == 'linux/arm/v7' && 'armv7' || 'arm64' }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
merge:
needs: [build]
runs-on: ubuntu-latest
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
pattern: digest-*
merge-multiple: true
path: /tmp/digests
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY_IMAGE }}
ghcr.io/${{ env.REGISTRY_IMAGE }}
tags: |
type=raw,value=latest
type=sha,format=short
- name: Create and push manifest list
env:
DOCKER_CLI_EXPERIMENTAL: enabled
run: |
# Extract Docker Hub tags
DH_TAGS=$(echo '${{ steps.meta.outputs.tags }}' | grep -v "^ghcr.io" | xargs -I {} echo "-t {}")
# Extract GitHub Container Registry tags
GHCR_TAGS=$(echo '${{ steps.meta.outputs.tags }}' | grep "^ghcr.io" | xargs -I {} echo "-t {}")
# Create a manifest list using the image digests from /tmp/digests/*
DIGESTS=$(for file in /tmp/digests/*; do
echo -n "${{ env.REGISTRY_IMAGE }}@$(cat $file) "
done)
# Create the manifest list for Docker Hub
docker buildx imagetools create $DH_TAGS $DIGESTS
# Create the manifest list for GitHub Container Registry
docker buildx imagetools create $GHCR_TAGS $DIGESTS
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:latest

View File

@ -1,4 +1,5 @@
name: Build Release
on:
push:
tags:
@ -8,95 +9,85 @@ jobs:
build-releases:
strategy:
matrix:
arch: ["x86_64", "armv7h", "aarch64"]
arch: ["linux/amd64 x86_64", "linux/arm/v7 armv7h", "linux/arm64 aarch64"]
name: Build ${{ matrix.arch }}
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Read info
id: tags
shell: bash
run: |
echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/}
echo ::set-output name=TAG::${GITHUB_REF/refs\/tags\//}
- name: Install dependencies
run: sudo apt update -y && sudo apt install -y qemu qemu-user-static
- name: Setup qemu-user-static
run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
arch="${{ matrix.arch }}"
echo "PLATFORM=${arch%% *}" >> $GITHUB_OUTPUT
echo "ARCH=${arch##* }" >> $GITHUB_OUTPUT
- name: Build ${{ matrix.arch }} release
run: |
mkdir artifacts
make docker-release-${{ matrix.arch }} ARCH=${{ matrix.arch }} VERSION=${{ steps.tags.outputs.version }} PREFIX="/usr"
docker buildx build --platform ${{ steps.tags.outputs.platform }} \
--build-arg VERSION=${{ steps.tags.outputs.version }} \
--build-arg ARCH=${{ steps.tags.outputs.arch }} \
--build-arg PREFIX="/usr" \
-t yay:${{ steps.tags.outputs.arch }} . --load
make docker-release ARCH=${{ steps.tags.outputs.arch }} VERSION=${{ steps.tags.outputs.version }} PREFIX="/usr"
mv *.tar.gz artifacts
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v4
with:
name: yay_${{ matrix.arch }}
name: yay_${{ steps.tags.outputs.arch }}
path: artifacts
create_release:
name: Create release from this build
needs: [build-releases]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Read info
id: tags
shell: bash
run: |
echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/}
echo ::set-output name=TAG::${GITHUB_REF/refs\/tags\//}
- name: Changelog
uses: scottbrenner/generate-changelog-action@master
id: changelog
env:
REPO: ${{ github.repository }}
- uses: actions/download-artifact@v2
echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- uses: actions/download-artifact@v4
with:
name: yay_x86_64
- uses: actions/download-artifact@v2
with:
name: yay_armv7h
- uses: actions/download-artifact@v2
with:
name: yay_aarch64
pattern: yay_*
merge-multiple: true
- name: Create Release
id: create_release
uses: actions/create-release@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.tags.outputs.tag }}
release_name: ${{ steps.tags.outputs.tag }}
body: |
${{ steps.changelog.outputs.changelog }}
draft: false
prerelease: false
- name: Upload x86_64 asset
id: upload-release-asset-x86_64
uses: actions/upload-release-asset@v1
run: |
gh release create ${{ steps.tags.outputs.tag }} \
--title "${{ steps.tags.outputs.tag }}" \
--generate-notes \
./yay_${{ steps.tags.outputs.version }}_*.tar.gz
- name: Release Notary Action
uses: docker://aevea/release-notary:latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./yay_${{ steps.tags.outputs.version }}_x86_64.tar.gz
asset_name: yay_${{ steps.tags.outputs.version }}_x86_64.tar.gz
asset_content_type: application/tar+gzip
- name: Upload armv7h asset
id: upload-release-asset-armv7h
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./yay_${{ steps.tags.outputs.version }}_armv7h.tar.gz
asset_name: yay_${{ steps.tags.outputs.version }}_armv7h.tar.gz
asset_content_type: application/tar+gzip
- name: Upload aarch64 asset
id: upload-release-asset-aarch64
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./yay_${{ steps.tags.outputs.version }}_aarch64.tar.gz
asset_name: yay_${{ steps.tags.outputs.version }}_aarch64.tar.gz
asset_content_type: application/tar+gzip

View File

@ -1,21 +0,0 @@
name: Tag
on:
push:
paths-ignore:
- ".github/**"
- "README.md"
- ".gitignore"
branches:
- master
jobs:
tag:
name: Tag release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: mathieudutour/github-tag-action@v4.5
id: tag_version
with:
github_token: ${{ secrets.PAT }}
tag_prefix: "v"

39
.github/workflows/testing-git.yml vendored Normal file
View File

@ -0,0 +1,39 @@
name: Test against pacman-git
on:
pull_request:
paths-ignore:
- "doc/**"
- "**/*.po"
- "README.md"
- ".gitignore"
jobs:
build:
name: Lint and test yay (-git)
runs-on: ubuntu-latest
container:
image: ghcr.io/jguer/yay-builder:latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- uses: actions/cache@v3
with:
path: /home/runner/work/yay/yay/pacman-git
key: ${{ runner.os }}-pacman-${{ hashFiles('/home/runner/work/yay/yay/pacman-git/PKGBUILD') }}
restore-keys: |
${{ runner.os }}-pacman-
- name: checkout pacman-git
run: |
git -C ./pacman-git pull || git clone https://aur.archlinux.org/pacman-git
useradd github
echo 'github ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
chmod -R 777 pacman-git
su github -c 'cd pacman-git; yes | makepkg -i --nocheck'
- name: Run Build and Tests with pacman-git
run: |
make test

View File

@ -1,13 +1,5 @@
name: Build
# This workflow is triggered on pushes to the repository.
name: Test against pacman
on:
push:
paths-ignore:
- "doc/**"
- "README.md"
- ".gitignore"
branches-ignore:
- "master"
pull_request:
jobs:
@ -15,19 +7,38 @@ jobs:
name: Lint and test yay
runs-on: ubuntu-latest
container:
image: samip537/archlinux:devel
image: ghcr.io/jguer/yay-builder:latest
steps:
- name: Checkout
uses: actions/checkout@v2
- uses: actions/cache@v1
- uses: actions/checkout@v4
- uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Run Build and tests
- name: Lint
env:
GOFLAGS: -buildvcs=false -tags=next
run: /app/bin/golangci-lint run -v ./...
- name: Run Build and Tests
run: make test
- name: Run Integration Tests
continue-on-error: true
run: |
pacman -Sy --noconfirm go
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.27.0
./bin/golangci-lint run ./...
make test
useradd -m yay &&
chown -R yay:yay . &&
cp -r ~/go/ /home/yay/go/ &&
chown -R yay:yay /home/yay/go/ &&
su yay -c "make test-integration"
- name: Build yay Artifact
env:
GOFLAGS: -buildvcs=false -tags=next
run: make
- name: Upload yay Artifact
uses: actions/upload-artifact@v4
with:
name: yay
path: ./yay
if-no-files-found: error
overwrite: true

6
.gitignore vendored
View File

@ -6,6 +6,7 @@
# Folders
_obj
_test
.vscode
*.cgo1.go
*.cgo2.c
@ -26,3 +27,8 @@ qemu-*
*.mo
*.pot
*.po~
*.pprof
node_modules/
xgotext
.devcontainer/

View File

@ -1,4 +1,31 @@
linters-settings:
version: "2"
run:
go: "1.20"
linters:
default: none
enable:
- bodyclose
- dogsled
- dupl
- errcheck
- errorlint
- gochecknoinits
- gocritic
- goprintffuncname
- gosec
- govet
- ineffassign
- lll
- misspell
- nakedret
- noctx
- nolintlint
- staticcheck
- unconvert
- unparam
- unused
- whitespace
settings:
dupl:
threshold: 100
funlen:
@ -14,89 +41,54 @@ linters-settings:
- opinionated
- performance
- style
disabled-checks:
- dupImport # https://github.com/go-critic/go-critic/issues/845
- ifElseChain
- octalLiteral
- whyNoLint
- wrapperFunc
gocyclo:
min-complexity: 15
goimports:
local-prefixes: github.com/Jguer/yay/v10
golint:
min-confidence: 0
gomnd:
settings:
mnd:
# don't include the "operation" and "assign"
checks: argument,case,condition,return
govet:
check-shadowing: true
lll:
line-length: 140
maligned:
suggest-new: true
misspell:
locale: US
linters:
# please, do not use `enable-all`: it's deprecated and will be removed soon.
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
disable-all: true
enable:
- bodyclose
- deadcode
- depguard
nolintlint:
require-explanation: false
require-specific: false
allow-unused: false
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- dupl
- errcheck
- errorlint
- gochecknoinits
- gocritic
- godot
- govet
- lll
- revive
- staticcheck
- wsl
path: (.+)_test.go
- path: (.+)\.go$
text: G204
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofmt
- goimports
- golint
- goprintffuncname
- gosec
- gosimple
- govet
- ineffassign
- interfacer
- lll
- misspell
- nakedret
- rowserrcheck
- staticcheck
- structcheck
- stylecheck
- typecheck
- unconvert
- unparam
- unused
- varcheck
- whitespace
- prealloc
- maligned
# disabled want to fix
#- scopelint
#- gomnd
#- goconst
#- gocyclo
#- funlen
#- dogsled
# disabled for now
# - godox
issues:
# Excluding configuration per-path, per-linter, per-text and per-source
exclude-rules:
- path: _test\.go
linters:
- gomnd
exclude:
- G204
- commentedOutCode
run:
tests: false
settings:
goimports:
local-prefixes:
- github.com/Jguer/yay/v12
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$

View File

@ -1,10 +1,29 @@
default_stages: [commit]
repos:
- repo: git://github.com/dnephin/pre-commit-golang
rev: master
- repo: https://github.com/dnephin/pre-commit-golang
rev: v0.5.1
hooks:
- id: go-fmt
- id: go-imports
args: [-local=github.com/Jguer/yay/v10/]
- id: golangci-lint
- id: go-unit-tests
- id: go-build
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.8 # Use the sha or tag you want to point at
hooks:
- id: prettier
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0 # Use the ref you want to point at
hooks:
- id: trailing-whitespace
- id: check-json
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/commitizen-tools/commitizen
rev: v3.15.0
hooks:
- id: commitizen
stages: [commit-msg]

7
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"go.lintTool": "golangci-lint",
"gopls": {
"formatting.gofumpt": true,
"formatting.local": "github.com/Jguer/yay/v12"
}
}

View File

@ -1,4 +1,22 @@
## Contributing to yay
# Contributing to yay
## Translation
[Transifex](https://www.transifex.com/yay-1/yay/)
## Quality Assurance
```sh
pacman -S --needed git base-devel
git clone https://aur.archlinux.org/yay-git.git
cd yay-git
makepkg -si
```
Installing `yay-git` and using issues to help determine what's broken is already
a very big help.
## Development
Contributors are always welcome!
@ -7,6 +25,15 @@ on, we suggest opening an issue detailing your ideas first.
Otherwise send us a pull request and we will be happy to review it.
### Vision
Yay is based on the design of [yaourt](https://github.com/archlinuxfr/yaourt), [apacman](https://github.com/oshazard/apacman) and [pacaur](https://github.com/rmarquis/pacaur). It is developed with these objectives in mind:
- Provide an interface for pacman
- Yaourt-style interactive search/install
- Minimal dependencies
- Minimize user input
### Dependencies
Yay depends on:
@ -14,27 +41,15 @@ Yay depends on:
- go (make only)
- git
- base-devel
- pacman
Note: Yay also depends on a few other projects (as vendored dependencies). These
projects are stored in `vendor/`, are built into yay at build time, and do not
need to be installed separately. These files are managed as go modules and should not be modified manually.
Following are the dependencies managed as go modules:
- https://github.com/Jguer/go-alpm
- https://github.com/Morganamilo/go-srcinfo
- https://github.com/Morganamilo/go-pacmanconf
- https://github.com/mikkeloscar/aur
Note: Yay also depends on a few other projects, these are pulled as go modules.
### Building
Run `make` to build Yay. This command will generate a binary called `yay` in
the same directory as the Makefile.
Note: Yay's Makefile sources its dependencies from `vendor/`. When
building manually, dependencies will instead be sourced from `GOPATH`. To
build against `vendor/` you must specify `-mod=vendor` in the build command.
#### Docker Release
`make docker-release` will build the release packages for `aarch64` and for `x86_64`.
@ -55,6 +70,9 @@ All code should be formatted through `go fmt`. This tool will automatically
format code for you. We recommend, however, that you write code in the proper
style and use `go fmt` only to catch mistakes.
Use [pre-commit](https://pre-commit.com/) to validate your commits against the various
linters configured for this repository.
### Testing
Run `make test` to test Yay. This command will verify that the code is

View File

@ -1,12 +1,12 @@
ARG BUILD_TAG=devel
FROM samip537/archlinux:${BUILD_TAG}
LABEL maintainer="Jguer,joaogg3 at google mail"
FROM ghcr.io/jguer/yay-builder:latest
LABEL maintainer="Jguer,docker@jguer.space"
ARG VERSION
ARG PREFIX
ARG ARCH
WORKDIR /app
RUN pacman -Sy --overwrite=* --needed --noconfirm \
go git
ENV ARCH=x86_64
COPY . .
RUN make release VERSION=${VERSION} PREFIX=${PREFIX} ARCH=${ARCH}

View File

@ -1,5 +1,5 @@
export GO111MODULE=on
GOPROXY ?= https://gocenter.io,direct
GOPROXY ?= https://proxy.golang.org,direct
export GOPROXY
BUILD_TAG = devel
@ -10,7 +10,7 @@ GO ?= go
PKGNAME := yay
PREFIX := /usr/local
MAJORVERSION := 10
MAJORVERSION := 12
MINORVERSION := 0
PATCHVERSION := 0
VERSION ?= ${MAJORVERSION}.${MINORVERSION}.${PATCHVERSION}
@ -18,14 +18,15 @@ VERSION ?= ${MAJORVERSION}.${MINORVERSION}.${PATCHVERSION}
LOCALEDIR := po
SYSTEMLOCALEPATH := $(PREFIX)/share/locale/
LANGS := pt pt_BR en es eu fr_FR pl_PL ru_RU zh_CN
# ls -1 po | sed -e 's/\.po$//' | paste -sd " "
LANGS := ca cs de en es eu fr_FR he id it_IT ja ko pl_PL pt_BR pt ru_RU ru sv tr uk zh_CN zh_TW
POTFILE := default.pot
POFILES := $(addprefix $(LOCALEDIR)/,$(addsuffix .po,$(LANGS)))
MOFILES := $(POFILES:.po=.mo)
GOFLAGS ?= -v -trimpath -mod=readonly -modcacherw
EXTRA_GOFLAGS ?= -buildmode=pie
LDFLAGS := $(LDFLAGS) -X "main.yayVersion=${VERSION}" -X "main.localePath=${SYSTEMLOCALEPATH}"
FLAGS ?= -trimpath -mod=readonly -modcacherw
EXTRA_FLAGS ?= -buildmode=pie
LDFLAGS := -X "main.yayVersion=${VERSION}" -X "main.localePath=${SYSTEMLOCALEPATH}" -linkmode=external -compressdwarf=false
RELEASE_DIR := ${PKGNAME}_${VERSION}_${ARCH}
PACKAGE := $(RELEASE_DIR).tar.gz
@ -41,14 +42,19 @@ all: | clean release
.PHONY: clean
clean:
$(GO) clean $(GOFLAGS) -i ./...
$(GO) clean $(FLAGS) -i ./...
rm -rf $(BIN) $(PKGNAME)_*
.PHONY: test_lint
test_lint: test lint
.PHONY: test
test:
$(GO) vet $(GOFLAGS) ./...
@test -z "$$(gofmt -l *.go)" || (echo "Files need to be linted. Use make fmt" && false)
$(GO) test $(GOFLAGS) --race -covermode=atomic . ./pkg/...
$(GO) test -race -covermode=atomic $(FLAGS) ./...
.PHONY: test-integration
test-integration:
$(GO) test -tags=integration $(FLAGS) ./...
.PHONY: build
build: $(BIN)
@ -62,24 +68,8 @@ docker-release-all:
make docker-release-x86_64 ARCH=x86_64
make docker-release-aarch64 ARCH=aarch64
.PHONY: docker-release-armv7h
docker-release-armv7h:
docker build --build-arg="BUILD_TAG=arm32v7-devel" -t yay-$(ARCH):${VERSION} .
docker run -e="ARCH=$(ARCH)" --name yay-$(ARCH) yay-$(ARCH):${VERSION} make release VERSION=${VERSION} PREFIX=${PREFIX}
docker cp yay-$(ARCH):/app/${PACKAGE} $(PACKAGE)
docker container rm yay-$(ARCH)
.PHONY: docker-release-aarch64
docker-release-aarch64:
docker build --build-arg="BUILD_TAG=arm64v8-devel" -t yay-$(ARCH):${VERSION} .
docker run -e="ARCH=$(ARCH)" --name yay-$(ARCH) yay-$(ARCH):${VERSION} make release VERSION=${VERSION} PREFIX=${PREFIX}
docker cp yay-$(ARCH):/app/${PACKAGE} $(PACKAGE)
docker container rm yay-$(ARCH)
.PHONY: docker-release-x86_64
docker-release-x86_64:
docker build --build-arg="BUILD_TAG=devel" -t yay-$(ARCH):${VERSION} .
docker run -e="ARCH=$(ARCH)" --name yay-$(ARCH) yay-$(ARCH):${VERSION} make release VERSION=${VERSION} PREFIX=${PREFIX}
docker-release:
docker create --name yay-$(ARCH) yay:${ARCH} /bin/sh
docker cp yay-$(ARCH):/app/${PACKAGE} $(PACKAGE)
docker container rm yay-$(ARCH)
@ -90,19 +80,9 @@ docker-build:
docker cp yay-$(ARCH):/app/${BIN} $(BIN)
docker container rm yay-$(ARCH)
.PHONY: test-vendor
test-vendor: vendor
@diff=$$(git diff vendor/); \
if [ -n "$$diff" ]; then \
echo "Please run 'make vendor' and commit the result:"; \
echo "$${diff}"; \
exit 1; \
fi;
.PHONY: lint
lint:
golangci-lint run
golint -set_exit_status . ./pkg/...
GOFLAGS="$(FLAGS)" golangci-lint run ./...
.PHONY: fmt
fmt:
@ -131,20 +111,24 @@ uninstall:
done
$(BIN): $(SOURCES)
$(GO) build $(GOFLAGS) -ldflags '-s -w $(LDFLAGS)' $(EXTRA_GOFLAGS) -o $@
$(GO) build $(FLAGS) -ldflags '$(LDFLAGS)' $(EXTRA_FLAGS) -o $@
$(RELEASE_DIR):
mkdir $(RELEASE_DIR)
$(PACKAGE): $(BIN) $(RELEASE_DIR) ${MOFILES}
strip ${BIN}
cp -t $(RELEASE_DIR) ${BIN} doc/${PKGNAME}.8 completions/* ${MOFILES}
tar -czvf $(PACKAGE) $(RELEASE_DIR)
locale:
xgotext -in . -out po
test -f $@ || msginit -l $* -i $< -o $@
msgmerge -U $@ $<
touch $@
mv po/default.pot po/en.po
for lang in ${LANGS}; do \
test -f po/$$lang.po || msginit --no-translator -l po/$$lang.po -i po/${POTFILE} -o po/$$lang.po; \
msgmerge -U po/$$lang.po po/${POTFILE}; \
touch po/$$lang.po; \
done
${LOCALEDIR}/%.mo: ${LOCALEDIR}/%.po
msgfmt $< -o $@

243
README.md
View File

@ -1,48 +1,162 @@
[![yay](https://img.shields.io/aur/version/yay?color=1793d1&label=yay&logo=arch-linux&style=for-the-badge)](https://aur.archlinux.org/packages/yay/)
[![yay-bin](https://img.shields.io/aur/version/yay-bin?color=1793d1&label=yay-bin&logo=arch-linux&style=for-the-badge)](https://aur.archlinux.org/packages/yay-bin/)
[![yay-git](https://img.shields.io/aur/version/yay-git?color=1793d1&label=yay-git&logo=arch-linux&style=for-the-badge)](https://aur.archlinux.org/packages/yay-git/)
![AUR votes](https://img.shields.io/aur/votes/yay?color=333333&style=for-the-badge)
[![GitHub license](https://img.shields.io/github/license/jguer/yay?color=333333&style=for-the-badge)](https://github.com/Jguer/yay/blob/master/LICENSE)
# Yay
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/github/license/jguer/yay.svg)](https://github.com/Jguer/yay/blob/master/LICENSE)
## Objectives
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 is based on the design of [yaourt](https://github.com/archlinuxfr/yaourt), [apacman](https://github.com/oshazard/apacman) and [pacaur](https://github.com/rmarquis/pacaur). It is developed with these objectives in mind:
- Provide an interface for pacman
- Yaourt-style interactive search/install
- Minimal dependencies
- Minimize user input
- Know when git packages are due for upgrades
### Help translate yay: [Transifex](https://www.transifex.com/yay-1/yay/)
## Features
- Perform advanced dependency solving
- Download PKGBUILDs from ABS or AUR
- Tab-complete the AUR
- Advanced dependency solving
- PKGBUILD downloading from ABS or AUR
- Completions for AUR packages
- Query user up-front for all input (prior to starting builds)
- Narrow search terms (`yay linux header` will first search `linux` and then narrow on `header`)
- Narrow search (`yay linux header` will first search `linux` and then narrow on `header`)
- Find matching package providers during search and allow selection
- Remove make dependencies at the end of the build process
- Run without sourcing PKGBUILD
- Build local PKGBUILDs with AUR dependencies
- Un/Vote for packages
[![asciicast](https://asciinema.org/a/399431.svg)](https://asciinema.org/a/399431)
[![asciicast](https://asciinema.org/a/399433.svg)](https://asciinema.org/a/399433)
## Installation
If you are migrating from another AUR helper, you can simply install Yay with that helper.
Alternatively, the initial installation of Yay can be done by cloning the PKGBUILD and
> [!WARNING]
> We are using `sudo` in these examples, you can switch that out for a different privilege escalation tool.
### Source
The initial installation of Yay can be done by cloning the PKGBUILD and
building with makepkg:
We make sure we have the `base-devel` package group installed.
```sh
sudo pacman -S --needed git base-devel
git clone https://aur.archlinux.org/yay.git
cd yay
makepkg -si
```
If you want to do all of this at once, we can chain the commands like so:
```sh
sudo pacman -S --needed git base-devel && git clone https://aur.archlinux.org/yay.git && cd yay && makepkg -si
```
### Binary
If you do not want to compile yay yourself you can use the builds generated by
GitHub Actions.
```sh
sudo pacman -S --needed git base-devel
git clone https://aur.archlinux.org/yay-bin.git
cd yay-bin
makepkg -si
```
If you want to do all of this at once, we can chain the commands like so:
```sh
sudo pacman -S --needed git base-devel && git clone https://aur.archlinux.org/yay-bin.git && cd yay-bin && makepkg -si
```
### Other distributions
If you're using Manjaro or [another distribution that packages `yay`](https://repology.org/project/yay/versions)
you can simply install yay using pacman (as root):
```sh
pacman -S --needed git base-devel yay
```
> [!WARNING]
> distributions sometimes lag updating yay on their repositories.
## First Use
#### Development packages upgrade
- Use `yay -Y --gendb` to generate a development package database for `*-git`
packages that were installed without yay.
This command should only be run once.
- `yay -Syu --devel` will then check for development package updates
- Use `yay -Y --devel --save` to make development package updates permanently
enabled (`yay` and `yay -Syu` will then always check dev packages)
## Examples of Custom Operations
| Command | Description |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `yay` | Alias to `yay -Syu`. |
| `yay <Search Term>` | Present package-installation selection menu. |
| `yay -Bi <dir>` | Install dependencies and build a local PKGBUILD. |
| `yay -G <AUR Package>` | Download PKGBUILD from ABS or AUR. (yay v12.0+) |
| `yay -Gp <AUR Package>` | Print to stdout PKGBUILD from ABS or AUR. |
| `yay -Ps` | Print system statistics. |
| `yay -Syu --devel` | Perform system upgrade, but also check for development package updates. |
| `yay -Syu --timeupdate` | Perform system upgrade and use PKGBUILD modification time (not version number) to determine update. |
| `yay -Wu <AUR Package>` | Unvote for package (Requires setting `AUR_USERNAME` and `AUR_PASSWORD` environment variables) (yay v11.3+) |
| `yay -Wv <AUR Package>` | Vote for package (Requires setting `AUR_USERNAME` and `AUR_PASSWORD` environment variables). (yay v11.3+) |
| `yay -Y --combinedupgrade --save` | Make combined upgrade the default mode. |
| `yay -Y --gendb` | Generate development package database used for devel update. |
| `yay -Yc` | Clean unneeded dependencies. |
## 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`
(see issue [#123](https://github.com/Jguer/yay/issues/123)).
- **Sometimes diffs are printed to the terminal, and other times they are paged via less. How do I fix this?**
Yay uses `git diff` to display diffs, which by default tells less not to
page if the output can fit into one terminal length. This behavior can be
overridden by exporting your own flags (`export LESS=SRX`).
- **Yay is not asking me to edit PKGBUILDS, and I don't like the diff menu! What can I do?**
`yay --editmenu --diffmenu=false --save`
- **How can I tell Yay to act only on AUR packages, or only on repo packages?**
`yay -{OPERATION} --aur`
`yay -{OPERATION} --repo`
- **A `Flagged Out Of Date AUR Packages` message is displayed. Why doesn't Yay update them?**
This message does not mean that updated AUR packages are available. It means
the packages have been flagged out of date on the AUR, but
their maintainers have not yet updated the `PKGBUILD`s
(see [outdated AUR packages](https://wiki.archlinux.org/index.php/Arch_User_Repository#Foo_in_the_AUR_is_outdated.3B_what_should_I_do.3F)).
- **Yay doesn't install dependencies added to a PKGBUILD during installation.**
Yay resolves all dependencies ahead of time. You are free to edit the
PKGBUILD in any way, but any problems you cause are your own and should not be
reported unless they can be reproduced with the original PKGBUILD.
- **I know my `-git` package has updates but yay doesn't offer to update it**
Yay uses a hash cache for development packages. Normally it is updated at the end of the package install with the message `Found git repo`.
If you transition between aur helpers and did not install the devel package using yay at some point, it is possible it never got added to the cache. `yay -Y --gendb` will fix the current version of every devel package and start checking from there.
- **I want to help out!**
Check [CONTRIBUTING.md](./CONTRIBUTING.md) for more information.
## Support
All support related to Yay should be requested via GitHub issues. Since Yay is not
@ -51,84 +165,25 @@ forums, AUR comments or other official channels.
A broken AUR package should be reported as a comment on the package's AUR page.
A package may only be considered broken if it fails to build with makepkg.
Reports should be made using makepkg and include the full output as well as any
other relevant information. Never make reports using Yay or any other external
tools.
## 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`
(see issue [#123](https://github.com/Jguer/yay/issues/123)).
#### Yay is not prompting to skip packages during system upgrade.
The default behavior was changed after
[v8.918](https://github.com/Jguer/yay/releases/tag/v8.918)
(see [3bdb534](https://github.com/Jguer/yay/commit/3bdb5343218d99d40f8a449b887348611f6bdbfc)
and issue [#554](https://github.com/Jguer/yay/issues/554)).
To restore the package-skip behavior use `--combinedupgrade` (make
it permanent by appending `--save`). Note: skipping packages will leave your
system in a
[partially-upgraded state](https://wiki.archlinux.org/index.php/System_maintenance#Partial_upgrades_are_unsupported).
#### Sometimes diffs are printed to the terminal, and other times they are paged via less. How do I fix this?
Yay uses `git diff` to display diffs, which by default tells less not to
page if the output can fit into one terminal length. This behavior can be
overridden by exporting your own flags (`export LESS=SRX`).
#### Yay is not asking me to edit PKGBUILDS, and I don't like the diff menu! What can I do?
`yay --editmenu --nodiffmenu --save`
#### How can I tell Yay to act only on AUR packages, or only on repo packages?
`yay -{OPERATION} --aur`
`yay -{OPERATION} --repo`
#### An `Out Of Date AUR Packages` message is displayed. Why doesn't Yay update them?
This message does not mean that updated AUR packages are available. It means
the packages have been flagged out of date on the AUR, but
their maintainers have not yet updated the `PKGBUILD`s
(see [outdated AUR packages](https://wiki.archlinux.org/index.php/Arch_User_Repository#Foo_in_the_AUR_is_outdated.3B_what_should_I_do.3F)).
#### Yay doesn't install dependencies added to a PKGBUILD during installation.
Yay resolves all dependencies ahead of time. You are free to edit the
PKGBUILD in any way, but any problems you cause are your own and should not be
reported unless they can be reproduced with the original PKGBUILD.
#### I know my `-git` package has updates but yay doesn't offer to update it
Yay uses an hash cache for development packages. Normally it is updated at the end of the package install with the message `Found git repo`.
If you transition between aur helpers and did not install the devel package using yay at some point, it is possible it never got added to the cache. `yay -Y --gendb` will fix the current version of every devel package and start checking from there.
#### I want to help out!
Check `CONTRIBUTING.md` for more information.
## Examples of Custom Operations
| Command | Description |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `yay <Search Term>` | Present package-installation selection menu. |
| `yay -Ps` | Print system statistics. |
| `yay -Yc` | Clean unneeded dependencies. |
| `yay -G <AUR Package>` | Download PKGBUILD from ABS or AUR. |
| `yay -Y --gendb` | Generate development package database used for devel update. |
| `yay -Syu --devel --timeupdate` | Perform system upgrade, but also check for development package updates and use PKGBUILD modification time (not version number) to determine update. |
## Images
<p float="left">
<img src="https://rawcdn.githack.com/Jguer/jguer.github.io/77647f396cb7156fd32e30970dbeaf6d6dc7f983/yay/yay.png" width="42%"/>
<img src="https://rawcdn.githack.com/Jguer/jguer.github.io/77647f396cb7156fd32e30970dbeaf6d6dc7f983/yay/yay-s.png" width="42%"/>
<p align="center">
<img src="https://raw.githubusercontent.com/Jguer/jguer.github.io/refs/heads/master/yay/yay.png" width="42%">
<img src="https://raw.githubusercontent.com/Jguer/jguer.github.io/refs/heads/master/yay/yay-s.png" width="42%">
</p>
<p float="left">
<img src="https://rawcdn.githack.com/Jguer/jguer.github.io/77647f396cb7156fd32e30970dbeaf6d6dc7f983/yay/yay-y.png" width="42%"/>
<img src="https://rawcdn.githack.com/Jguer/jguer.github.io/77647f396cb7156fd32e30970dbeaf6d6dc7f983/yay/yay-ps.png" width="42%"/>
<p align="center">
<img src="https://raw.githubusercontent.com/Jguer/jguer.github.io/refs/heads/master/yay/yay-y.png" width="42%">
<img src="https://raw.githubusercontent.com/Jguer/jguer.github.io/refs/heads/master/yay/yay-ps.png" width="42%">
</p>
### Other AUR helpers/tools
- [paru](https://github.com/morganamilo/paru)
- [aurutils](https://github.com/AladW/aurutils)
- [pikaur](https://github.com/actionless/pikaur)

13
SECURITY.md Normal file
View File

@ -0,0 +1,13 @@
# Security Policy
Thank you for helping keep yay secure!
## Supported Versions
We only provide security updates and support for the latest released version of yay. Please ensure you are using the most up-to-date version before reporting vulnerabilities.
## Reporting a Vulnerability
If you discover a security vulnerability, please email us at [security@jguer.space](mailto:security@jguer.space). We will respond as quickly as possible and coordinate a fix.
We appreciate responsible disclosure and your help in making this project safe for everyone.

View File

@ -1,103 +0,0 @@
package main
import (
"bufio"
"fmt"
"os"
"strconv"
alpm "github.com/Jguer/go-alpm"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v10/pkg/text"
)
func questionCallback(question alpm.QuestionAny) {
if qi, err := question.QuestionInstallIgnorepkg(); err == nil {
qi.SetInstall(true)
}
qp, err := question.QuestionSelectProvider()
if err != nil {
return
}
if hideMenus {
return
}
size := 0
_ = qp.Providers(alpmHandle).ForEach(func(pkg alpm.Package) error {
size++
return nil
})
str := gotext.Get("There are %d providers available for %s:", size, qp.Dep())
size = 1
var db string
_ = qp.Providers(alpmHandle).ForEach(func(pkg alpm.Package) error {
thisDB := pkg.DB().Name()
if db != thisDB {
db = thisDB
str += bold(cyan("\n:: ")) + bold(gotext.Get("Repository ")+db+"\n ")
}
str += fmt.Sprintf("%d) %s ", size, pkg.Name())
size++
return nil
})
text.OperationInfoln(str)
for {
fmt.Print(gotext.Get("\nEnter a number (default=1): "))
if config.NoConfirm {
fmt.Println()
break
}
reader := bufio.NewReader(os.Stdin)
numberBuf, overflow, err := reader.ReadLine()
if err != nil {
text.Errorln(err)
break
}
if overflow {
text.Errorln(gotext.Get(" Input too long"))
continue
}
if string(numberBuf) == "" {
break
}
num, err := strconv.Atoi(string(numberBuf))
if err != nil {
text.Errorln(gotext.Get("invalid number: %s", string(numberBuf)))
continue
}
if num < 1 || num > size {
text.Errorln(gotext.Get("invalid value: %d is not between %d and %d", num, 1, size))
continue
}
qp.SetUseIndex(num - 1)
break
}
}
func logCallback(level alpm.LogLevel, str string) {
switch level {
case alpm.LogWarning:
text.Warn(str)
case alpm.LogError:
text.Error(str)
}
}

15
ci.Dockerfile Normal file
View File

@ -0,0 +1,15 @@
FROM docker.io/ljmf00/archlinux:devel
LABEL maintainer="Jguer,docker@jguer.space"
ENV GO111MODULE=on
WORKDIR /app
RUN sed -i '/^\[community\]/,/^\[/ s/^/#/' /etc/pacman.conf
COPY go.mod .
RUN pacman-key --init && pacman -Sy && pacman -S --overwrite=* --noconfirm archlinux-keyring && \
pacman -Su --overwrite=* --needed --noconfirm pacman doxygen meson asciidoc go git gcc make sudo base-devel && \
rm -rfv /var/cache/pacman/* /var/lib/pacman/sync/* && \
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v2.1.5 && \
go mod download

179
clean.go
View File

@ -1,86 +1,76 @@
package main
import (
"fmt"
"io/ioutil"
"context"
"os"
"path/filepath"
"github.com/Jguer/aur"
mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v10/pkg/stringset"
"github.com/Jguer/yay/v10/pkg/text"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/runtime"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/settings/parser"
)
// GetPkgbuild gets the pkgbuild of the package 'pkg' trying the ABS first and then the AUR trying the ABS first and then the AUR.
// RemovePackage removes package from VCS information
func removeVCSPackage(pkgs []string) {
updated := false
for _, pkgName := range pkgs {
if _, ok := savedInfo[pkgName]; ok {
delete(savedInfo, pkgName)
updated = true
}
}
if updated {
err := saveVCSInfo()
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
}
// CleanDependencies removes all dangling dependencies in system
func cleanDependencies(removeOptional bool) error {
hanging, err := hangingPackages(removeOptional)
if err != nil {
return err
}
// CleanDependencies removes all dangling dependencies in system.
func cleanDependencies(ctx context.Context, cfg *settings.Configuration,
cmdBuilder exe.ICmdBuilder, cmdArgs *parser.Arguments, dbExecutor db.Executor,
removeOptional bool,
) error {
hanging := hangingPackages(removeOptional, dbExecutor)
if len(hanging) != 0 {
return cleanRemove(hanging)
return cleanRemove(ctx, cfg, cmdBuilder, cmdArgs, hanging)
}
return nil
}
// CleanRemove sends a full removal command to pacman with the pkgName slice
func cleanRemove(pkgNames []string) error {
// CleanRemove sends a full removal command to pacman with the pkgName slice.
func cleanRemove(ctx context.Context, cfg *settings.Configuration,
cmdBuilder exe.ICmdBuilder, cmdArgs *parser.Arguments, pkgNames []string,
) error {
if len(pkgNames) == 0 {
return nil
}
arguments := cmdArgs.copyGlobal()
_ = arguments.addArg("R")
arguments.addTarget(pkgNames...)
arguments := cmdArgs.CopyGlobal()
if err := arguments.AddArg("R", "s", "u"); err != nil {
return err
}
arguments.AddTarget(pkgNames...)
return show(passToPacman(arguments))
return cmdBuilder.Show(
cmdBuilder.BuildPacmanCmd(ctx,
arguments, cfg.Mode, settings.NoConfirm))
}
func syncClean(parser *arguments) error {
func syncClean(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
keepInstalled := false
keepCurrent := false
_, removeAll, _ := parser.getArg("c", "clean")
_, removeAll, _ := cmdArgs.GetArg("c", "clean")
for _, v := range pacmanConf.CleanMethod {
if v == "KeepInstalled" {
for _, v := range run.PacmanConf.CleanMethod {
switch v {
case "KeepInstalled":
keepInstalled = true
} else if v == "KeepCurrent" {
case "KeepCurrent":
keepCurrent = true
}
}
if mode == modeRepo || mode == modeAny {
if err := show(passToPacman(parser)); err != nil {
if run.Cfg.Mode.AtLeastRepo() {
if err := run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm)); err != nil {
return err
}
}
if !(mode == modeAUR || mode == modeAny) {
if !run.Cfg.Mode.AtLeastAUR() {
return nil
}
@ -91,10 +81,10 @@ func syncClean(parser *arguments) error {
question = gotext.Get("Do you want to remove all other AUR packages from cache?")
}
fmt.Println(gotext.Get("\nBuild directory:"), config.BuildDir)
run.Logger.Println(gotext.Get("\nBuild directory:"), run.Cfg.BuildDir)
if continueTask(question, true) {
if err := cleanAUR(keepInstalled, keepCurrent, removeAll); err != nil {
if run.Logger.ContinueTask(question, true, settings.NoConfirm) {
if err := cleanAUR(ctx, run, keepInstalled, keepCurrent, removeAll, dbExecutor); err != nil {
return err
}
}
@ -103,30 +93,30 @@ func syncClean(parser *arguments) error {
return nil
}
if continueTask(gotext.Get("Do you want to remove ALL untracked AUR files?"), true) {
return cleanUntracked()
if run.Logger.ContinueTask(gotext.Get("Do you want to remove ALL untracked AUR files?"), true, settings.NoConfirm) {
return cleanUntracked(ctx, run)
}
return nil
}
func cleanAUR(keepInstalled, keepCurrent, removeAll bool) error {
fmt.Println(gotext.Get("removing AUR packages from cache..."))
func cleanAUR(ctx context.Context, run *runtime.Runtime,
keepInstalled, keepCurrent, removeAll bool, dbExecutor db.Executor,
) error {
run.Logger.Println(gotext.Get("removing AUR packages from cache..."))
installedBases := make(stringset.StringSet)
inAURBases := make(stringset.StringSet)
installedBases := mapset.NewThreadUnsafeSet[string]()
inAURBases := mapset.NewThreadUnsafeSet[string]()
_, remotePackages, _, _, err := filterPackages()
if err != nil {
return err
}
remotePackages := dbExecutor.InstalledRemotePackages()
files, err := ioutil.ReadDir(config.BuildDir)
files, err := os.ReadDir(run.Cfg.BuildDir)
if err != nil {
return err
}
cachedPackages := make([]string, 0, len(files))
for _, file := range files {
if !file.IsDir() {
continue
@ -140,21 +130,23 @@ func cleanAUR(keepInstalled, keepCurrent, removeAll bool) error {
// Querying the AUR is slow and needs internet so don't do it if we
// don't need to.
if keepCurrent {
info, errInfo := aurInfo(cachedPackages, &aurWarnings{})
info, errInfo := run.AURClient.Get(ctx, &aur.Query{
Needles: cachedPackages,
})
if errInfo != nil {
return errInfo
}
for _, pkg := range info {
inAURBases.Set(pkg.PackageBase)
for i := range info {
inAURBases.Add(info[i].PackageBase)
}
}
for _, pkg := range remotePackages {
if pkg.Base() != "" {
installedBases.Set(pkg.Base())
installedBases.Add(pkg.Base())
} else {
installedBases.Set(pkg.Name())
installedBases.Add(pkg.Name())
}
}
@ -164,28 +156,29 @@ func cleanAUR(keepInstalled, keepCurrent, removeAll bool) error {
}
if !removeAll {
if keepInstalled && installedBases.Get(file.Name()) {
if keepInstalled && installedBases.Contains(file.Name()) {
continue
}
if keepCurrent && inAURBases.Get(file.Name()) {
if keepCurrent && inAURBases.Contains(file.Name()) {
continue
}
}
err = os.RemoveAll(filepath.Join(config.BuildDir, file.Name()))
if err != nil {
return nil
dir := filepath.Join(run.Cfg.BuildDir, file.Name())
run.Logger.Debugln("removing", dir)
if err = os.RemoveAll(dir); err != nil {
run.Logger.Warnln(gotext.Get("Unable to remove %s: %s", dir, err))
}
}
return nil
}
func cleanUntracked() error {
fmt.Println(gotext.Get("removing untracked AUR files from cache..."))
func cleanUntracked(ctx context.Context, run *runtime.Runtime) error {
run.Logger.Println(gotext.Get("removing untracked AUR files from cache..."))
files, err := ioutil.ReadDir(config.BuildDir)
files, err := os.ReadDir(run.Cfg.BuildDir)
if err != nil {
return err
}
@ -195,40 +188,20 @@ func cleanUntracked() error {
continue
}
dir := filepath.Join(config.BuildDir, file.Name())
if err := show(passToGit(dir, "clean", "-fx")); err != nil {
dir := filepath.Join(run.Cfg.BuildDir, file.Name())
run.Logger.Debugln("cleaning", dir)
if isGitRepository(dir) {
if err := run.CmdBuilder.Show(run.CmdBuilder.BuildGitCmd(ctx, dir, "clean", "-fx")); err != nil {
run.Logger.Warnln(gotext.Get("Unable to clean:"), dir)
return err
}
}
}
return nil
}
func cleanAfter(bases []Base) {
fmt.Println(gotext.Get("removing untracked AUR files from cache..."))
for i, base := range bases {
dir := filepath.Join(config.BuildDir, base.Pkgbase())
text.OperationInfoln(gotext.Get("Cleaning (%d/%d): %s", i+1, len(bases), cyan(dir)))
_, stderr, err := capture(passToGit(dir, "reset", "--hard", "HEAD"))
if err != nil {
text.Errorln(gotext.Get("error resetting %s: %s", base.String(), stderr))
}
if err := show(passToGit(dir, "clean", "-fx")); err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
}
func cleanBuilds(bases []Base) {
for i, base := range bases {
dir := filepath.Join(config.BuildDir, base.Pkgbase())
text.OperationInfoln(gotext.Get("Deleting (%d/%d): %s", i+1, len(bases), cyan(dir)))
if err := os.RemoveAll(dir); err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
func isGitRepository(dir string) bool {
_, err := os.Stat(filepath.Join(dir, ".git"))
return !os.IsNotExist(err)
}

116
clean_test.go Normal file
View File

@ -0,0 +1,116 @@
//go:build !integration
// +build !integration
package main
import (
"context"
"fmt"
"os/exec"
"strings"
"testing"
"github.com/Jguer/go-alpm/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/db/mock"
"github.com/Jguer/yay/v12/pkg/runtime"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/settings/parser"
)
func TestCleanHanging(t *testing.T) {
pacmanBin := t.TempDir() + "/pacman"
t.Parallel()
testCases := []struct {
name string
args []string
wantShow []string
}{
{
name: "clean",
args: []string{"Y", "c"},
wantShow: []string{"pacman", "-R", "-s", "-u", "--config", "/etc/pacman.conf", "--", "lsp-plugins"},
},
{
name: "clean double",
args: []string{"Y", "c", "c"},
wantShow: []string{"pacman", "-R", "-s", "-u", "--config", "/etc/pacman.conf", "--", "lsp-plugins", "linux-headers"},
},
}
dbExc := &mock.DBExecutor{
PackageOptionalDependsFn: func(i alpm.IPackage) []alpm.Depend {
if i.Name() == "linux" {
return []alpm.Depend{
{
Name: "linux-headers",
},
}
}
return []alpm.Depend{}
},
PackageProvidesFn: func(p alpm.IPackage) []alpm.Depend { return []alpm.Depend{} },
PackageDependsFn: func(p alpm.IPackage) []alpm.Depend { return []alpm.Depend{} },
LocalPackagesFn: func() []mock.IPackage {
return []mock.IPackage{
&mock.Package{
PReason: alpm.PkgReasonExplicit,
PName: "linux",
},
&mock.Package{
PReason: alpm.PkgReasonDepend,
PName: "lsp-plugins",
},
&mock.Package{
PReason: alpm.PkgReasonDepend,
PName: "linux-headers",
},
}
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mockRunner := &exe.MockRunner{
CaptureFn: func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return "", "", nil
},
ShowFn: func(cmd *exec.Cmd) error { return nil },
}
cmdBuilder := &exe.CmdBuilder{
SudoBin: "su",
PacmanBin: pacmanBin,
PacmanConfigPath: "/etc/pacman.conf",
GitBin: "git",
Runner: mockRunner,
SudoLoopEnabled: false,
}
run := &runtime.Runtime{CmdBuilder: cmdBuilder, Cfg: &settings.Configuration{}}
cmdArgs := parser.MakeArguments()
cmdArgs.AddArg(tc.args...)
err := handleCmd(context.Background(),
run, cmdArgs, dbExc,
)
require.NoError(t, err)
for i, call := range mockRunner.ShowCalls {
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, pacmanBin, "pacman")
// options are in a different order on different systems and on CI root user is used
assert.Subset(t, strings.Split(show, " "),
strings.Split(tc.wantShow[i], " "),
fmt.Sprintf("%d - %s", i, show))
}
})
}
}

542
cmd.go
View File

@ -2,22 +2,32 @@ package main
import (
"bufio"
"context"
"errors"
"fmt"
"net/http"
"os"
"strings"
alpm "github.com/Jguer/go-alpm"
alpm "github.com/Jguer/go-alpm/v2"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v10/pkg/completion"
"github.com/Jguer/yay/v10/pkg/intrange"
"github.com/Jguer/yay/v10/pkg/text"
"github.com/Jguer/yay/v12/pkg/completion"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/download"
"github.com/Jguer/yay/v12/pkg/intrange"
"github.com/Jguer/yay/v12/pkg/news"
"github.com/Jguer/yay/v12/pkg/query"
"github.com/Jguer/yay/v12/pkg/runtime"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
"github.com/Jguer/yay/v12/pkg/upgrade"
"github.com/Jguer/yay/v12/pkg/vcs"
)
var cmdArgs = makeArguments()
func usage() {
fmt.Println(`Usage:
func usage(logger *text.Logger) {
logger.Println(`Usage:
yay
yay <operation> [...]
yay <package(s)>
@ -34,15 +44,17 @@ operations:
yay {-U --upgrade} [options] <file(s)>
New operations:
yay {-Y --yay} [options] [package(s)]
yay {-B --build} [options] [dir]
yay {-G --getpkgbuild} [options] [package(s)]
yay {-P --show} [options]
yay {-G --getpkgbuild} [package(s)]
yay {-W --web} [options] [package(s)]
yay {-Y --yay} [options] [package(s)]
If no arguments are provided 'yay -Syu' will be performed
If no operation is provided -Y will be assumed
If no operation is specified 'yay -Syu' will be performed
If no operation is specified and targets are provided -Y will be assumed
New options:
--repo Assume targets are from the repositories
-N --repo Assume targets are from the repositories
-a --aur Assume targets are from the AUR
Permanent configuration options:
@ -50,8 +62,8 @@ Permanent configuration options:
config file when used
--aururl <url> Set an alternative AUR URL
--aurrpcurl <url> Set an alternative URL for the AUR /rpc endpoint
--builddir <dir> Directory used to download and run PKGBUILDS
--absdir <dir> Directory used to store downloads from the ABS
--editor <file> Editor to use when editing PKGBUILDs
--editorflags <flags> Pass arguments to editor
--makepkg <file> makepkg command to use
@ -80,22 +92,19 @@ Permanent configuration options:
--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
--askremovemake Ask to remove makedepends after install
--askyesremovemake Ask to remove makedepends after install("Y" as default)
--removemake Remove makedepends after install
--noremovemake Don't remove makedepends after install
--cleanafter Remove package sources after successful install
--nocleanafter Do not remove package sources after successful build
--keepsrc Keep pkg/ and src/ after building packages
--bottomup Shows AUR's packages first and then repository's
--topdown Shows repository's packages first and then AUR's
--singlelineresults List each search result on its own line
--doublelineresults List each search result on two lines, like pacman
--devel Check development packages during sysupgrade
--nodevel Do not check development packages
--rebuild Always build target packages
--rebuildall Always build all AUR packages
--norebuild Skip package build if in cache and up to date
@ -104,23 +113,14 @@ Permanent configuration options:
--noredownload Skip pkgbuild download if in cache and up to date
--redownloadall Always download pkgbuilds of all AUR packages
--provides Look for matching providers 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
--useask Automatically resolve conflicts using pacman's ask flag
--nouseask Confirm conflicts manually during the install
--combinedupgrade Refresh then perform the repo and AUR upgrade together
--nocombinedupgrade Perform the repo upgrade and AUR upgrade separately
--batchinstall Build multiple AUR packages then install them together
--nobatchinstall Build and install each AUR package one by one
--sudo <file> sudo command to use
--sudoflags <flags> Pass arguments to sudo
--sudoloop Loop sudo calls in the background to avoid timeout
--nosudoloop Do not loop sudo calls in the background
--timeupdate Check packages' AUR page for changes during sysupgrade
--notimeupdate Do not check packages' AUR page for changes
show specific options:
-c --complete Used for completions
@ -130,299 +130,309 @@ show specific options:
-w --news Print arch news
yay specific options:
-c --clean Remove unneeded dependencies
-c --clean Remove unneeded dependencies (-cc to ignore optdepends)
--gendb Generates development package DB used for updating
getpkgbuild specific options:
-f --force Force download for existing ABS packages`)
-f --force Force download for existing ABS packages
-p --print Print pkgbuild of packages`)
}
func handleCmd() error {
if cmdArgs.existsArg("h", "help") {
return handleHelp()
func handleCmd(ctx context.Context, run *runtime.Runtime,
cmdArgs *parser.Arguments, dbExecutor db.Executor,
) error {
if cmdArgs.ExistsArg("h", "help") {
return handleHelp(ctx, run, cmdArgs)
}
if config.SudoLoop && cmdArgs.needRoot() {
sudoLoopBackground()
if run.Cfg.SudoLoop && cmdArgs.NeedRoot(run.Cfg.Mode) {
run.CmdBuilder.SudoLoop()
}
switch cmdArgs.op {
switch cmdArgs.Op {
case "V", "version":
handleVersion()
handleVersion(run.Logger)
return nil
case "D", "database":
return show(passToPacman(cmdArgs))
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
case "F", "files":
return show(passToPacman(cmdArgs))
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
case "Q", "query":
return handleQuery()
return handleQuery(ctx, run, cmdArgs, dbExecutor)
case "R", "remove":
return handleRemove()
return handleRemove(ctx, run, cmdArgs, run.VCSStore)
case "S", "sync":
return handleSync()
return handleSync(ctx, run, cmdArgs, dbExecutor)
case "T", "deptest":
return show(passToPacman(cmdArgs))
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
case "U", "upgrade":
return show(passToPacman(cmdArgs))
return handleUpgrade(ctx, run, cmdArgs)
case "B", "build":
return handleBuild(ctx, run, dbExecutor, cmdArgs)
case "G", "getpkgbuild":
return handleGetpkgbuild()
return handleGetpkgbuild(ctx, run, cmdArgs, dbExecutor)
case "P", "show":
return handlePrint()
case "Y", "--yay":
return handleYay()
return handlePrint(ctx, run, cmdArgs, dbExecutor)
case "Y", "yay":
return handleYay(ctx, run, cmdArgs, run.CmdBuilder,
dbExecutor, run.QueryBuilder)
case "W", "web":
return handleWeb(ctx, run, cmdArgs)
}
return fmt.Errorf(gotext.Get("unhandled operation"))
return errors.New(gotext.Get("unhandled operation"))
}
func handleQuery() error {
if cmdArgs.existsArg("u", "upgrades") {
return printUpdateList(cmdArgs)
}
return show(passToPacman(cmdArgs))
}
// getFilter returns filter function which can keep packages which were only
// explicitly installed or ones installed as dependencies for showing available
// updates or their count.
func getFilter(cmdArgs *parser.Arguments) (upgrade.Filter, error) {
deps, explicit := cmdArgs.ExistsArg("d", "deps"), cmdArgs.ExistsArg("e", "explicit")
func handleHelp() error {
if cmdArgs.op == "Y" || cmdArgs.op == "yay" {
usage()
return nil
}
return show(passToPacman(cmdArgs))
}
func handleVersion() {
fmt.Printf("yay v%s - libalpm v%s\n", yayVersion, alpm.Version())
}
func handlePrint() (err error) {
switch {
case cmdArgs.existsArg("d", "defaultconfig"):
tmpConfig := defaultSettings()
tmpConfig.expandEnv()
fmt.Printf("%v", tmpConfig)
case cmdArgs.existsArg("g", "currentconfig"):
fmt.Printf("%v", config)
case cmdArgs.existsArg("n", "numberupgrades"):
err = printNumberOfUpdates()
case cmdArgs.existsArg("u", "upgrades"):
err = printUpdateList(cmdArgs)
case cmdArgs.existsArg("w", "news"):
err = printNewsFeed()
case cmdArgs.existsDouble("c", "complete"):
err = completion.Show(alpmHandle, config.AURURL, cacheHome, config.CompletionInterval, true)
case cmdArgs.existsArg("c", "complete"):
err = completion.Show(alpmHandle, config.AURURL, cacheHome, config.CompletionInterval, false)
case cmdArgs.existsArg("s", "stats"):
err = localStatistics()
default:
err = nil
case deps && explicit:
return nil, errors.New(gotext.Get("invalid option: '--deps' and '--explicit' may not be used together"))
case deps:
return func(pkg *upgrade.Upgrade) bool {
return pkg.Reason == alpm.PkgReasonDepend
}, nil
case explicit:
return func(pkg *upgrade.Upgrade) bool {
return pkg.Reason == alpm.PkgReasonExplicit
}, nil
}
return func(pkg *upgrade.Upgrade) bool {
return true
}, nil
}
func handleQuery(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
if cmdArgs.ExistsArg("u", "upgrades") {
filter, err := getFilter(cmdArgs)
if err != nil {
return err
}
func handleYay() error {
if cmdArgs.existsArg("gendb") {
return createDevelDB()
return printUpdateList(ctx, run, cmdArgs, dbExecutor,
cmdArgs.ExistsDouble("u", "sysupgrade"), filter)
}
if cmdArgs.existsDouble("c") {
return cleanDependencies(true)
if err := run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm)); err != nil {
if str := err.Error(); strings.Contains(str, "exit status") {
// yay -Qdt should not output anything in case of error
return fmt.Errorf("")
}
if cmdArgs.existsArg("c", "clean") {
return cleanDependencies(false)
}
if len(cmdArgs.targets) > 0 {
return handleYogurt()
return err
}
return nil
}
func handleGetpkgbuild() error {
return getPkgbuilds(cmdArgs.targets)
}
func handleYogurt() error {
config.SearchMode = numberMenu
return displayNumberMenu(cmdArgs.targets)
}
func handleSync() error {
targets := cmdArgs.targets
if cmdArgs.existsArg("s", "search") {
if cmdArgs.existsArg("q", "quiet") {
config.SearchMode = minimal
} else {
config.SearchMode = detailed
}
return syncSearch(targets)
}
if cmdArgs.existsArg("p", "print", "print-format") {
return show(passToPacman(cmdArgs))
}
if cmdArgs.existsArg("c", "clean") {
return syncClean(cmdArgs)
}
if cmdArgs.existsArg("l", "list") {
return syncList(cmdArgs)
}
if cmdArgs.existsArg("g", "groups") {
return show(passToPacman(cmdArgs))
}
if cmdArgs.existsArg("i", "info") {
return syncInfo(targets)
}
if cmdArgs.existsArg("u", "sysupgrade") {
return install(cmdArgs)
}
if len(cmdArgs.targets) > 0 {
return install(cmdArgs)
}
if cmdArgs.existsArg("y", "refresh") {
return show(passToPacman(cmdArgs))
}
func handleHelp(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments) error {
usage(run.Logger)
switch cmdArgs.Op {
case "Y", "yay", "G", "getpkgbuild", "P", "show", "W", "web", "B", "build":
return nil
}
func handleRemove() error {
err := show(passToPacman(cmdArgs))
run.Logger.Println("\npacman operation specific options:")
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
}
func handleVersion(logger *text.Logger) {
logger.Printf("yay v%s - libalpm v%s\n", yayVersion, alpm.Version())
}
func handlePrint(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
switch {
case cmdArgs.ExistsArg("d", "defaultconfig"):
tmpConfig := settings.DefaultConfig(yayVersion)
run.Logger.Printf("%v", tmpConfig)
return nil
case cmdArgs.ExistsArg("g", "currentconfig"):
run.Logger.Printf("%v", run.Cfg)
return nil
case cmdArgs.ExistsArg("w", "news"):
double := cmdArgs.ExistsDouble("w", "news")
quiet := cmdArgs.ExistsArg("q", "quiet")
return news.PrintNewsFeed(ctx, run.HTTPClient, run.Logger,
dbExecutor.LastBuildTime(), run.Cfg.BottomUp, double, quiet)
case cmdArgs.ExistsArg("c", "complete"):
return completion.Show(ctx, run.HTTPClient, dbExecutor,
run.Cfg.AURURL, run.Cfg.CompletionPath, run.Cfg.CompletionInterval, cmdArgs.ExistsDouble("c", "complete"))
case cmdArgs.ExistsArg("s", "stats"):
return localStatistics(ctx, run, dbExecutor)
}
return nil
}
func handleYay(ctx context.Context, run *runtime.Runtime,
cmdArgs *parser.Arguments, cmdBuilder exe.ICmdBuilder,
dbExecutor db.Executor, queryBuilder query.Builder,
) error {
switch {
case cmdArgs.ExistsArg("gendb"):
return createDevelDB(ctx, run, dbExecutor)
case cmdArgs.ExistsDouble("c"):
return cleanDependencies(ctx, run.Cfg, cmdBuilder, cmdArgs, dbExecutor, true)
case cmdArgs.ExistsArg("c", "clean"):
return cleanDependencies(ctx, run.Cfg, cmdBuilder, cmdArgs, dbExecutor, false)
case len(cmdArgs.Targets) > 0:
return displayNumberMenu(ctx, run, cmdArgs.Targets, dbExecutor, queryBuilder, cmdArgs)
}
return nil
}
func handleWeb(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments) error {
switch {
case cmdArgs.ExistsArg("v", "vote"):
return handlePackageVote(ctx, cmdArgs.Targets, run.AURClient, run.Logger,
run.VoteClient, true)
case cmdArgs.ExistsArg("u", "unvote"):
return handlePackageVote(ctx, cmdArgs.Targets, run.AURClient, run.Logger,
run.VoteClient, false)
}
return nil
}
func handleGetpkgbuild(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor download.DBSearcher) error {
if cmdArgs.ExistsArg("p", "print") {
return printPkgbuilds(dbExecutor, run.AURClient,
run.HTTPClient, run.Logger, cmdArgs.Targets, run.Cfg.Mode, run.Cfg.AURURL)
}
return getPkgbuilds(ctx, dbExecutor, run.AURClient, run,
cmdArgs.Targets, cmdArgs.ExistsArg("f", "force"))
}
func handleUpgrade(ctx context.Context,
run *runtime.Runtime, cmdArgs *parser.Arguments,
) error {
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
}
// -B* options
func handleBuild(ctx context.Context,
run *runtime.Runtime, dbExecutor db.Executor, cmdArgs *parser.Arguments,
) error {
if cmdArgs.ExistsArg("i", "install") {
return installLocalPKGBUILD(ctx, run, cmdArgs, dbExecutor)
}
return nil
}
func handleSync(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
targets := cmdArgs.Targets
switch {
case cmdArgs.ExistsArg("s", "search"):
return syncSearch(ctx, targets, dbExecutor, run.QueryBuilder, !cmdArgs.ExistsArg("q", "quiet"))
case cmdArgs.ExistsArg("p", "print", "print-format"):
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
case cmdArgs.ExistsArg("c", "clean"):
return syncClean(ctx, run, cmdArgs, dbExecutor)
case cmdArgs.ExistsArg("l", "list"):
return syncList(ctx, run, run.HTTPClient, cmdArgs, dbExecutor)
case cmdArgs.ExistsArg("g", "groups"):
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
case cmdArgs.ExistsArg("i", "info"):
return syncInfo(ctx, run, cmdArgs, targets, dbExecutor)
case cmdArgs.ExistsArg("u", "sysupgrade") || len(cmdArgs.Targets) > 0:
return syncInstall(ctx, run, cmdArgs, dbExecutor)
case cmdArgs.ExistsArg("y", "refresh"):
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
}
return nil
}
func handleRemove(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, localCache vcs.Store) error {
err := run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
if err == nil {
removeVCSPackage(cmdArgs.targets)
localCache.RemovePackages(cmdArgs.Targets)
}
return err
}
// NumberMenu presents a CLI for selecting packages to install.
func displayNumberMenu(pkgS []string) error {
var (
aurErr, repoErr error
aq aurQuery
pq repoQuery
lenaq, lenpq int
)
func displayNumberMenu(ctx context.Context, run *runtime.Runtime, pkgS []string, dbExecutor db.Executor,
queryBuilder query.Builder, cmdArgs *parser.Arguments,
) error {
queryBuilder.Execute(ctx, dbExecutor, pkgS)
pkgS = removeInvalidTargets(pkgS)
if mode == modeAUR || mode == modeAny {
aq, aurErr = narrowSearch(pkgS, true)
lenaq = len(aq)
}
if mode == modeRepo || mode == modeAny {
pq, repoErr = queryRepo(pkgS)
lenpq = len(pq)
if repoErr != nil {
return repoErr
}
}
if lenpq == 0 && lenaq == 0 {
return fmt.Errorf(gotext.Get("no packages match search"))
}
switch config.SortMode {
case topDown:
if mode == modeRepo || mode == modeAny {
pq.printSearch()
}
if mode == modeAUR || mode == modeAny {
aq.printSearch(lenpq + 1)
}
case bottomUp:
if mode == modeAUR || mode == modeAny {
aq.printSearch(lenpq + 1)
}
if mode == modeRepo || mode == modeAny {
pq.printSearch()
}
default:
return fmt.Errorf(gotext.Get("invalid sort mode. Fix with yay -Y --bottomup --save"))
}
if aurErr != nil {
text.Errorln(gotext.Get("Error during AUR search: %s\n", aurErr))
text.Warnln(gotext.Get("Showing repo packages only"))
}
text.Infoln(gotext.Get("Packages to install (eg: 1 2 3, 1-3 or ^4)"))
text.Info()
reader := bufio.NewReader(os.Stdin)
numberBuf, overflow, err := reader.ReadLine()
if err != nil {
if err := queryBuilder.Results(dbExecutor, query.NumberMenu); err != nil {
return err
}
if overflow {
return fmt.Errorf(gotext.Get("input too long"))
}
include, exclude, _, otherExclude := intrange.ParseNumberMenu(string(numberBuf))
arguments := cmdArgs.copyGlobal()
isInclude := len(exclude) == 0 && len(otherExclude) == 0
for i, pkg := range pq {
var target int
switch config.SortMode {
case topDown:
target = i + 1
case bottomUp:
target = len(pq) - i
default:
return fmt.Errorf(gotext.Get("invalid sort mode. Fix with yay -Y --bottomup --save"))
}
if (isInclude && include.Get(target)) || (!isInclude && !exclude.Get(target)) {
arguments.addTarget(pkg.DB().Name() + "/" + pkg.Name())
}
}
for i := range aq {
var target int
switch config.SortMode {
case topDown:
target = i + 1 + len(pq)
case bottomUp:
target = len(aq) - i + len(pq)
default:
return fmt.Errorf(gotext.Get("invalid sort mode. Fix with yay -Y --bottomup --save"))
}
if (isInclude && include.Get(target)) || (!isInclude && !exclude.Get(target)) {
arguments.addTarget("aur/" + aq[i].Name)
}
}
if len(arguments.targets) == 0 {
fmt.Println(gotext.Get(" there is nothing to do"))
if queryBuilder.Len() == 0 {
// no results were found
return nil
}
if config.SudoLoop {
sudoLoopBackground()
run.Logger.Infoln(gotext.Get("Packages to install (eg: 1 2 3, 1-3 or ^4)"))
numberBuf, err := run.Logger.GetInput("", false)
if err != nil {
return err
}
return install(arguments)
include, exclude, _, otherExclude := intrange.ParseNumberMenu(numberBuf)
targets, err := queryBuilder.GetTargets(include, exclude, otherExclude)
if err != nil {
return err
}
func syncList(parser *arguments) error {
// modify the arguments to pass for the install
cmdArgs.Targets = targets
if len(cmdArgs.Targets) == 0 {
run.Logger.Println(gotext.Get(" there is nothing to do"))
return nil
}
return syncInstall(ctx, run, cmdArgs, dbExecutor)
}
func syncList(ctx context.Context, run *runtime.Runtime,
httpClient *http.Client, cmdArgs *parser.Arguments, dbExecutor db.Executor,
) error {
aur := false
for i := len(parser.targets) - 1; i >= 0; i-- {
if parser.targets[i] == "aur" && (mode == modeAny || mode == modeAUR) {
parser.targets = append(parser.targets[:i], parser.targets[i+1:]...)
for i := len(cmdArgs.Targets) - 1; i >= 0; i-- {
if cmdArgs.Targets[i] == "aur" && run.Cfg.Mode.AtLeastAUR() {
cmdArgs.Targets = append(cmdArgs.Targets[:i], cmdArgs.Targets[i+1:]...)
aur = true
}
}
if (mode == modeAny || mode == modeAUR) && (len(parser.targets) == 0 || aur) {
localDB, err := alpmHandle.LocalDB()
if run.Cfg.Mode.AtLeastAUR() && (len(cmdArgs.Targets) == 0 || aur) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, run.Cfg.AURURL+"/packages.gz", http.NoBody)
if err != nil {
return err
}
resp, err := http.Get(config.AURURL + "/packages.gz")
resp, err := httpClient.Do(req)
if err != nil {
return err
}
@ -431,24 +441,26 @@ func syncList(parser *arguments) error {
scanner := bufio.NewScanner(resp.Body)
scanner.Scan()
for scanner.Scan() {
name := scanner.Text()
if cmdArgs.existsArg("q", "quiet") {
fmt.Println(name)
if cmdArgs.ExistsArg("q", "quiet") {
run.Logger.Println(name)
} else {
fmt.Printf("%s %s %s", magenta("aur"), bold(name), bold(green(gotext.Get("unknown-version"))))
run.Logger.Printf("%s %s %s", text.Magenta("aur"), text.Bold(name), text.Bold(text.Green(gotext.Get("unknown-version"))))
if localDB.Pkg(name) != nil {
fmt.Print(bold(blue(gotext.Get(" [Installed]"))))
if dbExecutor.LocalPackage(name) != nil {
run.Logger.Print(text.Bold(text.Blue(gotext.Get(" [Installed]"))))
}
fmt.Println()
run.Logger.Println()
}
}
}
if (mode == modeAny || mode == modeRepo) && (len(parser.targets) != 0 || !aur) {
return show(passToPacman(parser))
if run.Cfg.Mode.AtLeastRepo() && (len(cmdArgs.Targets) != 0 || !aur) {
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
}
return nil

140
cmd_test.go Normal file
View File

@ -0,0 +1,140 @@
//go:build !integration
// +build !integration
package main
import (
"context"
"fmt"
"io"
"os"
"os/exec"
"strings"
"testing"
"github.com/Jguer/aur"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/db/mock"
mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
"github.com/Jguer/yay/v12/pkg/query"
"github.com/Jguer/yay/v12/pkg/runtime"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
"github.com/Jguer/yay/v12/pkg/vcs"
)
func TestYogurtMenuAURDB(t *testing.T) {
t.Skip("skip until Operation service is an interface")
t.Parallel()
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
gitBin := t.TempDir() + "/git"
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return "", "", nil
}
showOverride := func(cmd *exec.Cmd) error {
return nil
}
mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
PacmanConfigPath: "/etc/pacman.conf",
GitBin: "git",
Runner: mockRunner,
SudoLoopEnabled: false,
}
cmdArgs := parser.MakeArguments()
cmdArgs.AddArg("Y")
cmdArgs.AddTarget("yay")
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
RefreshHandleFn: func() error {
return nil
},
ReposFn: func() []string {
return []string{"aur"}
},
SyncPackagesFn: func(s ...string) []mock.IPackage {
return []mock.IPackage{
&mock.Package{
PName: "yay",
PBase: "yay",
PVersion: "10.0.0",
PDB: mock.NewDB("aur"),
},
}
},
LocalPackageFn: func(s string) mock.IPackage {
return nil
},
}
aurCache := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{
{
Name: "yay",
PackageBase: "yay",
Version: "10.0.0",
},
}, nil
},
}
logger := text.NewLogger(io.Discard, os.Stderr, strings.NewReader("1\n"), true, "test")
run := &runtime.Runtime{
Cfg: &settings.Configuration{
RemoveMake: "no",
},
Logger: logger,
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
QueryBuilder: query.NewSourceQueryBuilder(aurCache, logger, "votes", parser.ModeAny, "name",
true, false, true),
AURClient: aurCache,
}
err = handleCmd(context.Background(), run, cmdArgs, db)
require.NoError(t, err)
wantCapture := []string{}
wantShow := []string{
"pacman -S -y --config /etc/pacman.conf --",
"pacman -S -y -u --config /etc/pacman.conf --",
}
require.Len(t, mockRunner.ShowCalls, len(wantShow))
require.Len(t, mockRunner.CaptureCalls, len(wantCapture))
for i, call := range mockRunner.ShowCalls {
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
show = strings.ReplaceAll(show, gitBin, "pacman")
// options are in a different order on different systems and on CI root user is used
assert.Subset(t, strings.Split(show, " "), strings.Split(wantShow[i], " "), fmt.Sprintf("%d - %s", i, show))
}
}

View File

@ -51,7 +51,7 @@ _pacman_repo_list() {
_yay() {
compopt -o default
local common core cur database files prev query remove sync upgrade o
local yays show getpkgbuild
local yays show getpkgbuild web
local cur prev words cword
_init_completion || return
@ -61,28 +61,29 @@ _yay() {
search unrequired upgrades' 'c e g i k l m n o p s t u')
remove=('cascade dbonly nodeps assume-installed nosave print recursive unneeded' 'c n p s u')
sync=('asdeps asexplicit clean dbonly downloadonly overwrite groups ignore ignoregroup
info list needed nodeps assume-installed print refresh recursive search sysupgrade'
'c g i l p s u w y')
info list needed nodeps assume-installed print refresh recursive search sysupgrade aur repo'
'c g i l p s u w y a N')
upgrade=('asdeps asexplicit overwrite needed nodeps assume-installed print recursive' 'p')
core=('database files help query remove sync upgrade version' 'D F Q R S U V h')
##yay stuff
common=('arch cachedir color config confirm dbpath debug gpgdir help hookdir logfile
noconfirm noprogressbar noscriptlet quiet root verbose
makepkg pacman git gpg gpgflags config requestsplitn sudoloop nosudoloop
redownload noredownload redownloadall rebuild rebuildall rebuildtree norebuild
sortby answerclean answerdiff answeredit answerupgrade noanswerclean noanswerdiff
noansweredit noanswerupgrade cleanmenu diffmenu editmenu upgrademenu cleanafter nocleanafter
nocleanmenu nodiffmenu noupgrademenu provides noprovides pgpfetch nopgpfetch
useask nouseask combinedupgrade nocombinedupgrade aur repo makepkgconf
nomakepkgconf askremovemake removemake noremovemake completioninterval aururl
searchby batchinstall nobatchinstall'
makepkg pacman git gpg gpgflags config requestsplitn sudoloop
redownload noredownload redownloadall rebuild rebuildall rebuildtree norebuild sortby
singlelineresults doublelineresults answerclean answerdiff answeredit answerupgrade noanswerclean noanswerdiff
noansweredit noanswerupgrade cleanmenu diffmenu editmenu cleanafter keepsrc
provides pgpfetch
useask combinedupgrade aur repo makepkgconf
nomakepkgconf askremovemake askyesremovemake removemake noremovemake completioninterval aururl aurrpcurl
searchby batchinstall'
'b d h q r v')
yays=('clean gendb' 'c')
show=('complete defaultconfig currentconfig stats news' 'c d g s w')
getpkgbuild=('force' 'f')
getpkgbuild=('force print' 'f p')
web=('vote unvote' 'v u')
for o in 'D database' 'F files' 'Q query' 'R remove' 'S sync' 'U upgrade' 'Y yays' 'P show' 'G getpkgbuild'; do
for o in 'D database' 'F files' 'Q query' 'R remove' 'S sync' 'U upgrade' 'Y yays' 'P show' 'G getpkgbuild' 'W web'; do
_arch_incomp "$o" && break
done
@ -119,6 +120,9 @@ _yay() {
G)
_yay_pkg
;;
W)
_yay_pkg
;;
esac
fi
true
@ -126,7 +130,7 @@ _yay() {
_pacman_file() {
compopt -o filenames
_filedir 'pkg.tar*'
_filedir 'pkg.*'
}
complete -F _yay yay

View File

@ -5,10 +5,10 @@
set -l progname yay
# Yay constants
set -l noopt 'not __fish_contains_opt -s Y -s G -s V -s P -s S -s D -s Q -s R -s U -s T -s F database query sync remove upgrade deptest files version'
set -l listall "(yay -Pc)"
set -l listpacman "(__fish_print_packages)"
set -l yayspecific '__fish_contains_opt -s Y yay'
set -l webspecific '__fish_contains_opt -s W web'
set -l show '__fish_contains_opt -s P show'
set -l getpkgbuild '__fish_contains_opt -s G getpkgbuild'
@ -16,6 +16,8 @@ set -l getpkgbuild '__fish_contains_opt -s G getpkgbuild'
set -l listinstalled "(pacman -Q | string replace ' ' \t)"
set -l listrepos "(__fish_print_pacman_repos)"
set -l listgroups "(pacman -Sg)\t'Package Group'"
set -l noopt 'not __fish_contains_opt -s S -s D -s Q -s R -s U -s T -s F -s Y -s W -s P -s G database query sync remove upgrade deptest files show getpkgbuild web yay'
set -l database '__fish_contains_opt -s D database'
set -l query '__fish_contains_opt -s Q query'
set -l remove '__fish_contains_opt -s R remove'
@ -23,6 +25,8 @@ set -l sync '__fish_contains_opt -s S sync'
set -l upgrade '__fish_contains_opt -s U upgrade'
set -l files '__fish_contains_opt -s F files'
complete -c $progname -e
complete -c $progname -f
# HACK: We only need these two to coerce fish to stop file completion and complete options
@ -35,9 +39,9 @@ complete -c $progname -s Q -f -l query -n "$noopt" -d 'Query the package databas
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 dependencies'
complete -c $progname -s U -f -l upgrade -n "$noopt" -d 'Upgrade or add a local package'
complete -c $progname -s U -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 V -f -l version -n "$noopt" -d 'Display version and exit'
complete -c $progname -s V -f -l version -d 'Display version and exit'
complete -c $progname -s h -f -l help -d 'Display help'
# General options
@ -48,7 +52,7 @@ complete -c $progname -n "not $noopt" -s v -l verbose -d 'Output more status mes
complete -c $progname -n "not $noopt" -l arch -d 'Alternate architecture' -f
complete -c $progname -n "not $noopt" -l cachedir -d 'Alternate package cache location' -xa "(__fish_complete_directories)"
complete -c $progname -n "not $noopt" -l color -d 'Colorize the output' -fa '{auto,always,never}'
complete -c $progname -n "not $noopt" -l config -d 'Alternate config file' -r
complete -c $progname -n "not $noopt" -l config -d 'Alternate config file' -rF
complete -c $progname -n "not $noopt" -l confirm -d 'Always ask for confirmation' -f
complete -c $progname -n "not $noopt" -l debug -d 'Display debug messages' -f
complete -c $progname -n "not $noopt" -l disable-download-timeout -d 'Use relaxed timeouts for download' -f
@ -96,7 +100,7 @@ for condition in sync upgrade
complete -c $progname -n "$$condition" -l ignore -d 'Ignore a package upgrade (can be used more than once)' -xa "$listall"
complete -c $progname -n "$$condition" -l ignoregroup -d 'Ignore a group upgrade (can be used more than once)' -xa "$listgroups"
complete -c $progname -n "$$condition" -l needed -d 'Do not reinstall up to date packages' -f
complete -c $progname -n "$$condition" -l overwrite -d 'Overwrite conflicting files (can be used more than once)' -r
complete -c $progname -n "$$condition" -l overwrite -d 'Overwrite conflicting files (can be used more than once)' -rF
end
# Database options
@ -110,7 +114,7 @@ complete -c $progname -n "$has_db_opt; and $database" -xa "$listinstalled"
# File options - since pacman 5
complete -c $progname -n "$files" -s x -l regex -d 'Interpret each query as a regular expression' -f
complete -c $progname -n "$files" -l machinereadable -d 'Print each match in a machine readable output format' -f
complete -c $progname -n "$files" -d 'Package' -xa "$listpacman"
complete -c $progname -n "$files" -d Package -xa "$listpacman"
# Query options
complete -c $progname -n "$query" -s c -l changelog -d 'View the change log of PACKAGE' -f
@ -120,8 +124,8 @@ complete -c $progname -n "$query" -s i -l info -d 'View PACKAGE [backup files] i
complete -c $progname -n "$query" -s k -l check -d 'Check that PACKAGE files exist' -f
complete -c $progname -n "$query" -s m -l foreign -d 'List installed packages not found in sync database' -f
complete -c $progname -n "$query" -s n -l native -d 'list installed packages only found in sync database' -f
complete -c $progname -n "$query" -s o -l owns -d 'Query the package that owns FILE' -r
complete -c $progname -n "$query" -s p -l file -d 'Query a package file instead of the database' -r
complete -c $progname -n "$query" -s o -l owns -d 'Query the package that owns FILE' -rF
complete -c $progname -n "$query" -s p -l file -d 'Query a package file instead of the database' -rF
complete -c $progname -n "$query" -s s -l search -d 'Search locally-installed packages for regexp' -f
complete -c $progname -n "$query" -s t -l unrequired -d 'List only unrequired packages [and optdepends]' -f
complete -c $progname -n "$query" -s u -l upgrades -d 'List only out-of-date packages' -f
@ -145,18 +149,24 @@ complete -c $progname -n "$sync" -xa "$listall $listgroups"
# Upgrade options
# Theoretically, pacman reads packages in all formats that libarchive supports
# In practice, it's going to be tar.xz, tar.gz or tar.zst
complete -c $progname -n "$upgrade" -xa '(__fish_complete_suffix pkg.tar.zst; __fish_complete_suffix pkg.tar.xz; __fish_complete_suffix pkg.tar.gz)' -d 'Package file'
# In practice, it's going to be tar.xz, tar.gz, tar.zst, or just pkg.tar (uncompressed pkg)
complete -c $progname -n "$upgrade" -xa '(__fish_complete_suffix pkg.tar.zst; __fish_complete_suffix pkg.tar.xz; __fish_complete_suffix pkg.tar.gz; __fish_complete_suffix pkg.tar;)' -d 'Package file'
# Yay operations
complete -c $progname -s Y -f -l yay -n "$noopt" -d 'Yay specific operations'
complete -c $progname -s P -f -l show -n "$noopt" -d 'Print information'
complete -c $progname -s G -f -l getpkgbuild -n "$noopt" -d 'Get PKGBUILD from ABS or AUR'
complete -c $progname -s W -f -l web -n "$noopt" -d 'Web operations'
# Web options
complete -c $progname -n "$webspecific" -s v -l vote -d 'Vote for AUR packages' -f
complete -c $progname -n "$webspecific" -s u -l unvote -d 'Unvote for AUR packages' -f
complete -c $progname -n "$webspecific" -xa "$listall"
# New options
complete -c $progname -n "not $noopt" -l repo -d 'Assume targets are from the AUR' -f
complete -c $progname -n "not $noopt" -s a -l aur -d 'Assume targets are from the repositories' -f
complete -c $progname -n "not $noopt" -s a -l aur -d 'Assume targets are from the AUR' -f
complete -c $progname -n "not $noopt" -s N -l repo -d 'Assume targets are from the repositories' -f
# Yay options
complete -c $progname -n "$yayspecific" -s c -l clean -d 'Remove unneeded dependencies' -f
@ -174,12 +184,13 @@ complete -c $progname -n "$show" -s q -l quiet -d 'Do not print news description
# Getpkgbuild options
complete -c $progname -n "$getpkgbuild" -s f -l force -d 'Force download for existing ABS packages' -f
complete -c $progname -n "$getpkgbuild" -xa "$listall"
complete -c $progname -n "$getpkgbuild" -s p -l print -d 'Print pkgbuild of packages' -f
# Premenent configuration settings
# Permanent configuration settings
complete -c $progname -n "not $noopt" -l save -d 'Save current arguments to yay permanent configuration' -f
complete -c $progname -n "not $noopt" -l aururl -d 'Set an alternative AUR URL' -f
complete -c $progname -n "not $noopt" -l aurrpcurl -d 'Set an alternative URL for the AUR /rpc endpoint' -f
complete -c $progname -n "not $noopt" -l builddir -d 'Directory to use for Building AUR Packages' -r
complete -c $progname -n "not $noopt" -l absdir -d 'Directory used to store downloads from the ABS' -r
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
@ -205,35 +216,26 @@ complete -c $progname -n "not $noopt" -l noanswerupgrade -d 'Unset the answer fo
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 askremovemake -d 'Ask to remove make deps after install' -f
complete -c $progname -n "not $noopt" -l askyesremovemake -d 'Ask to remove make deps after install(with "Y" as default)' -f
complete -c $progname -n "not $noopt" -l removemake -d 'Remove make deps after install' -f
complete -c $progname -n "not $noopt" -l noremovemake -d 'Do not remove make deps after install' -f
complete -c $progname -n "not $noopt" -l topdown -d 'Shows repository packages first and then aur' -f
complete -c $progname -n "not $noopt" -l bottomup -d 'Shows aur packages first and then repository' -f
complete -c $progname -n "not $noopt" -l singlelineresults -d 'List each search result on its own line' -f
complete -c $progname -n "not $noopt" -l doublelineresults -d 'List each search result on two lines, like pacman' -f
complete -c $progname -n "not $noopt" -l devel -d 'Check -git/-svn/-hg development version' -f
complete -c $progname -n "not $noopt" -l nodevel -d 'Disable development version checking' -f
complete -c $progname -n "not $noopt" -l cleanafter -d 'Clean package sources after successful build' -f
complete -c $progname -n "not $noopt" -l nocleanafter -d 'Disable package sources cleaning' -f
complete -c $progname -n "not $noopt" -l keepsrc -d 'Keep pkg/ and src/ after building packages' -f
complete -c $progname -n "not $noopt" -l timeupdate -d 'Check package modification date and version' -f
complete -c $progname -n "not $noopt" -l notimeupdate -d 'Check only package version change' -f
complete -c $progname -n "not $noopt" -l redownload -d 'Redownload PKGBUILD of package even if up-to-date' -f
complete -c $progname -n "not $noopt" -l redownloadall -d 'Redownload PKGBUILD of package and deps even if up-to-date' -f
complete -c $progname -n "not $noopt" -l noredownload -d 'Do not redownload up-to-date PKGBUILDs' -f
complete -c $progname -n "not $noopt" -l provides -d 'Look for matching providers when searching for packages' -f
complete -c $progname -n "not $noopt" -l noprovides -d 'Just look for packages by pkgname' -f
complete -c $progname -n "not $noopt" -l pgpfetch -d 'Prompt to import PGP keys from PKGBUILDs' -f
complete -c $progname -n "not $noopt" -l nopgpfetch -d 'Do not prompt to import PGP keys' -f
complete -c $progname -n "not $noopt" -l useask -d 'Automatically resolve conflicts using pacmans ask flag' -f
complete -c $progname -n "not $noopt" -l nouseask -d 'Confirm conflicts manually during the install' -f
complete -c $progname -n "not $noopt" -l combinedupgrade -d 'Refresh then perform the repo and AUR upgrade together' -f
complete -c $progname -n "not $noopt" -l nocombinedupgrade -d 'Perform the repo upgrade and AUR upgrade separately' -f
complete -c $progname -n "not $noopt" -l batchinstall -d 'Build multiple AUR packages then install them together' -f
complete -c $progname -n "not $noopt" -l nobatchinstall -d 'Build and install each AUR package one by one' -f
complete -c $progname -n "not $noopt" -l rebuild -d 'Always build target packages' -f
complete -c $progname -n "not $noopt" -l rebuildall -d 'Always build all AUR packages' -f
complete -c $progname -n "not $noopt" -l rebuildtree -d 'Always build all AUR packages even if installed' -f
@ -241,4 +243,3 @@ complete -c $progname -n "not $noopt" -l norebuild -d 'Skip package build if in
complete -c $progname -n "not $noopt" -l mflags -d 'Pass the following options to makepkg' -f
complete -c $progname -n "not $noopt" -l gpgflags -d 'Pass the following options to gpg' -f
complete -c $progname -n "not $noopt" -l sudoloop -d 'Loop sudo calls in the background to avoid timeout' -f
complete -c $progname -n "not $noopt" -l nosudoloop -d 'Do not loop sudo calls in the background' -f

View File

@ -1,5 +1,5 @@
#compdef yay
# vim:fdm=marker foldlevel=0 tabstop=2 shiftwidth=2 filetype=zsh
# vim:tabstop=2 shiftwidth=2 filetype=zsh
typeset -A opt_args
setopt extendedglob
@ -16,15 +16,17 @@ _pacman_opts_commands=(
{-T,--deptest}'[Check if dependencies are installed]'
{-U,--upgrade}'[Upgrade a package]'
{-Y,--yay}'[Yay specific options]'
{-W,--web}'[web options]'
{-V,--version}'[Display version and exit]'
'(-h --help)'{-h,--help}'[Display usage]'
)
# options for passing to _arguments: options common to all commands
_pacman_opts_common=(
'--repo[Assume targets are from the repositories]'
{-N,--repo}'[Assume targets are from the repositories]'
{-a,--aur}'[Assume targets are from the AUR]'
'--aururl[Set an alternative AUR URL]:url'
'--aurrpcurl[Set an alternative URL for the AUR /rpc endpoint]:url'
'--arch[Set an alternate architecture]'
{-b,--dbpath}'[Alternate database location]:database_location:_files -/'
'--color[colorize the output]:color options:(always never auto)'
@ -68,46 +70,36 @@ _pacman_opts_common=(
'--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]"
"--askremovemake[Ask to remove makedepends after install]"
"--askyesremovemake[Ask to remove makedepends after install(with "Y" as default)]"
"--removemake[Remove makedepends after install]"
"--noremovemake[Don't remove makedepends after install]"
'--bottomup[Show AUR packages first]'
'--topdown[Show repository packages first]'
'--singlelineresults[List each search result on its own line]'
'--doublelineresults[List each search result on two lines, like pacman]'
'--devel[Check -git/-svn/-hg development version]'
'--nodevel[Disable development version checking]'
'--cleanafter[Clean package sources after successful build]'
'--nocleanafter[Disable package sources cleaning after successful build]'
'--keepsrc[Keep pkg/ and src/ after building packages]'
'--timeupdate[Check packages modification date and version]'
'--notimeupdate[Check only package version change]'
'--redownload[Always download pkgbuilds of targets]'
'--redownloadall[Always download pkgbuilds of all AUR packages]'
'--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 providers 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]"
"--useask[Automatically resolve conflicts using pacman's ask flag]"
'--nouseask[Confirm conflicts manually during the install]'
'--combinedupgrade[Refresh then perform the repo and AUR upgrade together]'
'--nocombinedupgrade[Perform the repo upgrade and AUR upgrade separately]'
'--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'
'--gpgflags[Pass arguments to gpg]:gpgflags'
'--sudoloop[Loop sudo calls in the background to avoid timeout]'
'--nosudoloop[Do not loop sudo calls in the background]'
'--searchby[Search for packages using a specified field]'
'--sortby[Sort AUR results by a specific field during search]'
'--batchinstall[Build multiple AUR packages then install them together]'
'--nobatchinstall[Build and install each AUR package one by one]'
)
# options for passing to _arguments: options for --upgrade commands
@ -160,6 +152,13 @@ _pacman_opts_yay_modifiers=(
# -G
_pacman_opts_getpkgbuild_modifiers=(
{-f,--force}'[Force download for existing ABS packages]'
{-p,--print}'[Print PKGBUILDs]:package:_pacman_completions_all_packages'
)
# -W
_pacman_opts_web_modifiers=(
{-u,--unvote}'[Unvote AUR package]:package:_pacman_completions_all_packages'
{-v,--vote}'[Vote AUR package]:package:_pacman_completions_all_packages'
)
# -P
@ -501,16 +500,19 @@ _pacman_zsh_comp() {
"$_pacman_opts_query_modifiers[@]" \
'*:package file:_files -g "*.pkg.tar*~*.sig(.,@)"'
;;
T*)
_pacman_action_deptest
;;
Q*)
_pacman_action_query
;;
P*)
_arguments -s : \
'-P' \
"$_pacman_opts_print_modifiers[@]"
;;
W*)
_arguments -s : \
'-W' \
"$_pacman_opts_web_modifiers[@]"
;;
R*)
_pacman_action_remove
;;
@ -541,10 +543,7 @@ _pacman_zsh_comp() {
_pacman_action_sync
;;
T*)
_arguments -s : \
'-T' \
"$_pacman_opts_common[@]" \
":packages:_pacman_all_packages"
_pacman_action_deptest
;;
U*)
_pacman_action_upgrade
@ -554,10 +553,12 @@ _pacman_zsh_comp() {
;;
Y*)
_arguments -s : \
'-Y' \
"$_pacman_opts_yay_modifiers[@]"
;;
G*)
_arguments -s : \
'-G' \
"$_pacman_opts_getpkgbuild_modifiers[@]"
;;

444
config.go
View File

@ -1,444 +0,0 @@
package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"os"
"os/exec"
"strings"
alpm "github.com/Jguer/go-alpm"
pacmanconf "github.com/Morganamilo/go-pacmanconf"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v10/pkg/text"
)
// Verbosity settings for search
const (
numberMenu = iota
detailed
minimal
)
const (
// Describes Sorting method for numberdisplay
bottomUp = iota
topDown
)
const (
modeAUR targetMode = iota
modeRepo
modeAny
)
type targetMode int
// Configuration stores yay's config.
type Configuration struct {
AURURL string `json:"aururl"`
BuildDir string `json:"buildDir"`
ABSDir string `json:"absdir"`
Editor string `json:"editor"`
EditorFlags string `json:"editorflags"`
MakepkgBin string `json:"makepkgbin"`
MakepkgConf string `json:"makepkgconf"`
PacmanBin string `json:"pacmanbin"`
PacmanConf string `json:"pacmanconf"`
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"`
GpgBin string `json:"gpgbin"`
GpgFlags string `json:"gpgflags"`
MFlags string `json:"mflags"`
SortBy string `json:"sortby"`
SearchBy string `json:"searchby"`
GitFlags string `json:"gitflags"`
RemoveMake string `json:"removemake"`
SudoBin string `json:"sudobin"`
SudoFlags string `json:"sudoflags"`
RequestSplitN int `json:"requestsplitn"`
SearchMode int `json:"-"`
SortMode int `json:"sortmode"`
CompletionInterval int `json:"completionrefreshtime"`
SudoLoop bool `json:"sudoloop"`
TimeUpdate bool `json:"timeupdate"`
NoConfirm bool `json:"-"`
Devel bool `json:"devel"`
CleanAfter bool `json:"cleanAfter"`
Provides bool `json:"provides"`
PGPFetch bool `json:"pgpfetch"`
UpgradeMenu bool `json:"upgrademenu"`
CleanMenu bool `json:"cleanmenu"`
DiffMenu bool `json:"diffmenu"`
EditMenu bool `json:"editmenu"`
CombinedUpgrade bool `json:"combinedupgrade"`
UseAsk bool `json:"useask"`
BatchInstall bool `json:"batchinstall"`
}
var yayVersion = "10.0.0"
var localePath = "/usr/share/locale"
// configFileName holds the name of the config file.
const configFileName string = "config.json"
// vcsFileName holds the name of the vcs file.
const vcsFileName string = "vcs.json"
// configHome handles config directory home
var configHome string
// cacheHome handles cache home
var cacheHome string
// savedInfo holds the current vcs info
var savedInfo vcsInfo
// configfile holds yay config file path.
var configFile string
// vcsfile holds yay vcs info file path.
var vcsFile string
// shouldSaveConfig holds whether or not the config should be saved
var shouldSaveConfig bool
// YayConf holds the current config values for yay.
var config *Configuration
// AlpmConf holds the current config values for pacman.
var pacmanConf *pacmanconf.Config
// 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 = modeAny
var hideMenus = false
// SaveConfig writes yay config to file.
func (config *Configuration) saveConfig() error {
marshalledinfo, err := json.MarshalIndent(config, "", "\t")
if err != nil {
return err
}
in, err := os.OpenFile(configFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return err
}
defer in.Close()
if _, err = in.Write(marshalledinfo); err != nil {
return err
}
return in.Sync()
}
func defaultSettings() *Configuration {
newConfig := &Configuration{
AURURL: "https://aur.archlinux.org",
BuildDir: "$HOME/.cache/yay",
ABSDir: "$HOME/.cache/yay/abs",
CleanAfter: false,
Editor: "",
EditorFlags: "",
Devel: false,
MakepkgBin: "makepkg",
MakepkgConf: "",
NoConfirm: false,
PacmanBin: "pacman",
PGPFetch: true,
PacmanConf: "/etc/pacman.conf",
GpgFlags: "",
MFlags: "",
GitFlags: "",
SortMode: bottomUp,
CompletionInterval: 7,
SortBy: "votes",
SearchBy: "name-desc",
SudoLoop: false,
GitBin: "git",
GpgBin: "gpg",
SudoBin: "sudo",
SudoFlags: "",
TimeUpdate: false,
RequestSplitN: 150,
ReDownload: "no",
ReBuild: "no",
BatchInstall: false,
AnswerClean: "",
AnswerDiff: "",
AnswerEdit: "",
AnswerUpgrade: "",
RemoveMake: "ask",
Provides: true,
UpgradeMenu: true,
CleanMenu: true,
DiffMenu: true,
EditMenu: false,
UseAsk: false,
CombinedUpgrade: false,
}
if os.Getenv("XDG_CACHE_HOME") != "" {
newConfig.BuildDir = "$XDG_CACHE_HOME/yay"
}
return newConfig
}
func (config *Configuration) expandEnv() {
config.AURURL = os.ExpandEnv(config.AURURL)
config.ABSDir = os.ExpandEnv(config.ABSDir)
config.BuildDir = os.ExpandEnv(config.BuildDir)
config.Editor = os.ExpandEnv(config.Editor)
config.EditorFlags = os.ExpandEnv(config.EditorFlags)
config.MakepkgBin = os.ExpandEnv(config.MakepkgBin)
config.MakepkgConf = os.ExpandEnv(config.MakepkgConf)
config.PacmanBin = os.ExpandEnv(config.PacmanBin)
config.PacmanConf = os.ExpandEnv(config.PacmanConf)
config.GpgFlags = os.ExpandEnv(config.GpgFlags)
config.MFlags = os.ExpandEnv(config.MFlags)
config.GitFlags = os.ExpandEnv(config.GitFlags)
config.SortBy = os.ExpandEnv(config.SortBy)
config.SearchBy = os.ExpandEnv(config.SearchBy)
config.GitBin = os.ExpandEnv(config.GitBin)
config.GpgBin = os.ExpandEnv(config.GpgBin)
config.SudoBin = os.ExpandEnv(config.SudoBin)
config.SudoFlags = os.ExpandEnv(config.SudoFlags)
config.ReDownload = os.ExpandEnv(config.ReDownload)
config.ReBuild = os.ExpandEnv(config.ReBuild)
config.AnswerClean = os.ExpandEnv(config.AnswerClean)
config.AnswerDiff = os.ExpandEnv(config.AnswerDiff)
config.AnswerEdit = os.ExpandEnv(config.AnswerEdit)
config.AnswerUpgrade = os.ExpandEnv(config.AnswerUpgrade)
config.RemoveMake = os.ExpandEnv(config.RemoveMake)
}
// Editor returns the preferred system editor.
func editor() (editor string, args []string) {
switch {
case config.Editor != "":
editor, err := exec.LookPath(config.Editor)
if err != nil {
fmt.Fprintln(os.Stderr, err)
} else {
return editor, strings.Fields(config.EditorFlags)
}
fallthrough
case os.Getenv("EDITOR") != "":
if editorArgs := strings.Fields(os.Getenv("EDITOR")); len(editorArgs) != 0 {
editor, err := exec.LookPath(editorArgs[0])
if err != nil {
fmt.Fprintln(os.Stderr, err)
} else {
return editor, editorArgs[1:]
}
}
fallthrough
case os.Getenv("VISUAL") != "":
if editorArgs := strings.Fields(os.Getenv("VISUAL")); len(editorArgs) != 0 {
editor, err := exec.LookPath(editorArgs[0])
if err != nil {
fmt.Fprintln(os.Stderr, err)
} else {
return editor, editorArgs[1:]
}
}
fallthrough
default:
fmt.Fprintln(os.Stderr)
text.Errorln(gotext.Get("%s is not set", bold(cyan("$EDITOR"))))
text.Warnln(gotext.Get("Add %s or %s to your environment variables", bold(cyan("$EDITOR")), bold(cyan("$VISUAL"))))
for {
text.Infoln(gotext.Get("Edit PKGBUILD with?"))
editorInput, err := getInput("")
if err != nil {
fmt.Fprintln(os.Stderr, err)
continue
}
editorArgs := strings.Fields(editorInput)
if len(editorArgs) == 0 {
continue
}
editor, err := exec.LookPath(editorArgs[0])
if err != nil {
fmt.Fprintln(os.Stderr, err)
continue
}
return editor, editorArgs[1:]
}
}
}
// ContinueTask prompts if user wants to continue task.
// If NoConfirm is set the action will continue without user input.
func continueTask(s string, cont bool) bool {
if config.NoConfirm {
return cont
}
var response string
var postFix string
yes := gotext.Get("yes")
no := gotext.Get("no")
y := string([]rune(yes)[0])
n := string([]rune(no)[0])
if cont {
postFix = fmt.Sprintf(" [%s/%s] ", strings.ToUpper(y), n)
} else {
postFix = fmt.Sprintf(" [%s/%s] ", y, strings.ToUpper(n))
}
text.Info(bold(s), bold(postFix))
if _, err := fmt.Scanln(&response); err != nil {
return cont
}
response = strings.ToLower(response)
return response == yes || response == y
}
func getInput(defaultValue string) (string, error) {
text.Info()
if defaultValue != "" || config.NoConfirm {
fmt.Println(defaultValue)
return defaultValue, nil
}
reader := bufio.NewReader(os.Stdin)
buf, overflow, err := reader.ReadLine()
if err != nil {
return "", err
}
if overflow {
return "", fmt.Errorf(gotext.Get("input too long"))
}
return string(buf), nil
}
func (config *Configuration) String() string {
var buf bytes.Buffer
enc := json.NewEncoder(&buf)
enc.SetIndent("", "\t")
if err := enc.Encode(config); err != nil {
fmt.Fprintln(os.Stderr, err)
}
return buf.String()
}
func toUsage(usages []string) alpm.Usage {
if len(usages) == 0 {
return alpm.UsageAll
}
var ret alpm.Usage
for _, usage := range usages {
switch usage {
case "Sync":
ret |= alpm.UsageSync
case "Search":
ret |= alpm.UsageSearch
case "Install":
ret |= alpm.UsageInstall
case "Upgrade":
ret |= alpm.UsageUpgrade
case "All":
ret |= alpm.UsageAll
}
}
return ret
}
func configureAlpm() error {
// TODO: set SigLevel
// sigLevel := alpm.SigPackage | alpm.SigPackageOptional | alpm.SigDatabase | alpm.SigDatabaseOptional
// localFileSigLevel := alpm.SigUseDefault
// remoteFileSigLevel := alpm.SigUseDefault
for _, repo := range pacmanConf.Repos {
// TODO: set SigLevel
db, err := alpmHandle.RegisterSyncDB(repo.Name, 0)
if err != nil {
return err
}
db.SetServers(repo.Servers)
db.SetUsage(toUsage(repo.Usage))
}
if err := alpmHandle.SetCacheDirs(pacmanConf.CacheDir); err != nil {
return err
}
// add hook directories 1-by-1 to avoid overwriting the system directory
for _, dir := range pacmanConf.HookDir {
if err := alpmHandle.AddHookDir(dir); err != nil {
return err
}
}
if err := alpmHandle.SetGPGDir(pacmanConf.GPGDir); err != nil {
return err
}
if err := alpmHandle.SetLogFile(pacmanConf.LogFile); err != nil {
return err
}
if err := alpmHandle.SetIgnorePkgs(pacmanConf.IgnorePkg); err != nil {
return err
}
if err := alpmHandle.SetIgnoreGroups(pacmanConf.IgnoreGroup); err != nil {
return err
}
if err := alpmHandle.SetArch(pacmanConf.Architecture); err != nil {
return err
}
if err := alpmHandle.SetNoUpgrades(pacmanConf.NoUpgrade); err != nil {
return err
}
if err := alpmHandle.SetNoExtracts(pacmanConf.NoExtract); err != nil {
return err
}
/*if err := alpmHandle.SetDefaultSigLevel(sigLevel); err != nil {
return err
}
if err := alpmHandle.SetLocalFileSigLevel(localFileSigLevel); err != nil {
return err
}
if err := alpmHandle.SetRemoteFileSigLevel(remoteFileSigLevel); err != nil {
return err
}*/
if err := alpmHandle.SetUseSyslog(pacmanConf.UseSyslog); err != nil {
return err
}
return alpmHandle.SetCheckSpace(pacmanConf.CheckSpace)
}

View File

@ -1,59 +0,0 @@
package main
import (
"reflect"
"testing"
)
func expect(t *testing.T, field string, a interface{}, b interface{}, err error) {
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(a, b) {
t.Errorf("%s expected: %s got %s", field, a, b)
}
}
func TestConfig(t *testing.T) {
config = &Configuration{}
config.PacmanConf = "./testdata/pacman.conf"
err := initAlpm()
if err != nil {
t.Fatal(err)
}
h := alpmHandle
root, err := h.Root()
expect(t, "RootDir", "/", root, err)
cache, err := h.CacheDirs()
expect(t, "CacheDir", []string{"/cachedir/", "/another/"}, cache.Slice(), err)
log, err := h.LogFile()
expect(t, "LogFile", "/logfile", log, err)
gpg, err := h.GPGDir()
expect(t, "GPGDir", "/gpgdir/", gpg, err)
hook, err := h.HookDirs()
expect(t, "HookDir", []string{"/usr/share/libalpm/hooks/", "/hookdir/"}, hook.Slice(), err)
arch, err := h.Arch()
expect(t, "Architecture", "8086", arch, err)
ignorePkg, err := h.IgnorePkgs()
expect(t, "IgnorePkg", []string{"ignore", "this", "package"}, ignorePkg.Slice(), err)
ignoreGroup, err := h.IgnoreGroups()
expect(t, "IgnoreGroup", []string{"ignore", "this", "group"}, ignoreGroup.Slice(), err)
noUp, err := h.NoUpgrades()
expect(t, "NoUpgrade", []string{"noupgrade"}, noUp.Slice(), err)
noEx, err := h.NoExtracts()
expect(t, "NoExtract", []string{"noextract"}, noEx.Slice(), err)
check, err := h.CheckSpace()
expect(t, "CheckSpace", true, check, err)
}

176
dep.go
View File

@ -1,176 +0,0 @@
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) (pkg, mod, ver string) {
split := strings.FieldsFunc(dep, func(c rune) bool {
match := c == '>' || c == '<' || c == '='
if match {
mod += string(c)
}
return match
})
if len(split) == 0 {
return "", "", ""
}
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) (db, name string) {
split := strings.SplitN(pkg, "/", 2)
if len(split) == 2 {
return split[0], split[1]
}
return "", split[0]
}
func getBases(pkgs []*rpc.Pkg) []Base {
basesMap := make(map[string]Base)
for _, pkg := range pkgs {
basesMap[pkg.PackageBase] = append(basesMap[pkg.PackageBase], pkg)
}
bases := make([]Base, 0, len(basesMap))
for _, base := range basesMap {
bases = append(bases, base)
}
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-")
}
func isDevelPackage(pkg alpm.Package) bool {
return isDevelName(pkg.Name()) || isDevelName(pkg.Base())
}

View File

@ -1,293 +0,0 @@
package main
import (
"fmt"
"os"
"strings"
"sync"
alpm "github.com/Jguer/go-alpm"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v10/pkg/stringset"
"github.com/Jguer/yay/v10/pkg/text"
)
func (dp *depPool) checkInnerConflict(name, conflict string, conflicts stringset.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, conflict string, conflicts stringset.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, conflict string, conflicts stringset.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 stringset.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 stringset.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 stringset.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() (stringset.MapStringSet, error) {
var wg sync.WaitGroup
innerConflicts := make(stringset.MapStringSet)
conflicts := make(stringset.MapStringSet)
wg.Add(2)
text.OperationInfoln(gotext.Get("Checking for conflicts..."))
go func() {
dp.checkForwardConflicts(conflicts)
dp.checkReverseConflicts(conflicts)
wg.Done()
}()
text.OperationInfoln(gotext.Get("Checking for inner conflicts..."))
go func() {
dp.checkInnerConflicts(innerConflicts)
wg.Done()
}()
wg.Wait()
if len(innerConflicts) != 0 {
text.Errorln(gotext.Get("\nInner 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)
}
}
if len(conflicts) != 0 {
text.Errorln(gotext.Get("\nPackage conflicts found:"))
for name, pkgs := range conflicts {
str := gotext.Get("%s Installing %s will remove:", red(bold(smallArrow)), cyan(name))
for pkg := range pkgs {
str += " " + cyan(pkg) + ","
}
str = strings.TrimSuffix(str, ",")
fmt.Println(str)
}
}
// Add the inner conflicts to the conflicts
// These are used to decide what to pass --ask to (if set) or don't pass --noconfirm to
// As we have no idea what the order is yet we add every inner conflict to the slice
for name, pkgs := range innerConflicts {
conflicts[name] = make(stringset.StringSet)
for pkg := range pkgs {
conflicts[pkg] = make(stringset.StringSet)
}
}
if len(conflicts) > 0 {
if !config.UseAsk {
if config.NoConfirm {
return nil, fmt.Errorf(gotext.Get("package conflicts can not be resolved with noconfirm, aborting"))
}
text.Errorln(gotext.Get("Conflicting packages will have to be confirmed manually"))
}
}
return conflicts, nil
}
type missing struct {
Good stringset.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.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
}
text.Errorln(gotext.Get("Could not find all required packages:"))
for dep, trees := range missing.Missing {
for _, tree := range trees {
fmt.Fprintf(os.Stderr, "\t%s", cyan(dep))
if len(tree) == 0 {
fmt.Fprint(os.Stderr, gotext.Get(" (Target"))
} else {
fmt.Fprint(os.Stderr, gotext.Get(" (Wanted by: "))
for n := 0; n < len(tree)-1; n++ {
fmt.Fprint(os.Stderr, cyan(tree[n]), " -> ")
}
fmt.Fprint(os.Stderr, cyan(tree[len(tree)-1]))
}
fmt.Fprintln(os.Stderr, ")")
}
}
return fmt.Errorf("")
}

View File

@ -1,141 +0,0 @@
package main
import (
alpm "github.com/Jguer/go-alpm"
rpc "github.com/mikkeloscar/aur"
"github.com/Jguer/yay/v10/pkg/stringset"
)
// Base is an AUR base package
type Base []*rpc.Pkg
// Pkgbase returns the first base package.
func (b Base) Pkgbase() string {
return b[0].PackageBase
}
// Version returns the first base package version.
func (b Base) Version() string {
return b[0].Version
}
// URLPath returns the first base package URL.
func (b Base) URLPath() string {
return b[0].URLPath
}
type depOrder struct {
Aur []Base
Repo []*alpm.Package
Runtime stringset.StringSet
}
func makeDepOrder() *depOrder {
return &depOrder{
make([]Base, 0),
make([]*alpm.Package, 0),
make(stringset.StringSet),
}
}
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)
}
}
}
for i, base := range do.Aur {
if base.Pkgbase() == pkg.PackageBase {
do.Aur[i] = append(base, pkg)
return
}
}
do.Aur = append(do.Aur, Base{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.Aur {
lenAur += len(base)
}
return len(do.Runtime) != lenAur+len(do.Repo)
}
func (do *depOrder) getMake() []string {
makeOnly := []string{}
for _, base := range do.Aur {
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
}

View File

@ -1,481 +0,0 @@
package main
import (
"sort"
"strings"
"sync"
alpm "github.com/Jguer/go-alpm"
rpc "github.com/mikkeloscar/aur"
"github.com/Jguer/yay/v10/pkg/stringset"
)
type target struct {
DB string
Name string
Mod string
Version string
}
func toTarget(pkg string) target {
db, dep := splitDBFromName(pkg)
name, mod, depVersion := splitDep(dep)
return target{
DB: db,
Name: name,
Mod: mod,
Version: depVersion,
}
}
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.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.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.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 := dp.SyncDB.FindGroupPkgs(target.Name)
if !group.Empty() {
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.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.Search(strings.Join(words[:i+1], "-"))
if err == nil {
break
}
}
if err != nil {
return
}
for iR := range results {
mux.Lock()
if _, ok := dp.AurCache[results[iR].Name]; !ok {
pkgs.Set(results[iR].Name)
}
mux.Unlock()
}
}
for pkg := range pkgs {
if dp.LocalDB.Pkg(pkg) != nil {
continue
}
wg.Add(1)
go doSearch(pkg)
}
wg.Wait()
return nil
}
func (dp *depPool) cacheAURPackages(_pkgs stringset.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.StringSet, explicit bool) error {
newPackages := make(stringset.StringSet)
newAURPackages := make(stringset.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
}
_, isInstalled := dp.LocalDB.PkgCache().FindSatisfier(dep) // has satisfier installed: skip
hm := hideMenus
hideMenus = isInstalled == nil
repoPkg, inRepos := dp.SyncDB.FindSatisfier(dep) // has satisfier in repo: fetch it
hideMenus = hm
if isInstalled == nil && (config.ReBuild != "tree" || inRepos == nil) {
continue
}
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.StringSet)
providerSlice := makeProviders(depName)
if dp.LocalDB.Pkg(depName) != 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) {
providerSlice.Pkgs = append(providerSlice.Pkgs, pkg)
seen.Set(pkg.Name)
continue
}
for _, provide := range pkg.Provides {
if provideSatisfies(provide, dep) {
providerSlice.Pkgs = append(providerSlice.Pkgs, pkg)
seen.Set(pkg.Name)
continue
}
}
}
if !config.Provides && providerSlice.Len() >= 1 {
return providerSlice.Pkgs[0]
}
if providerSlice.Len() == 1 {
return providerSlice.Pkgs[0]
}
if providerSlice.Len() > 1 {
sort.Sort(providerSlice)
return providerMenu(dep, providerSlice)
}
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
}

180
doc/yay.8
View File

@ -1,5 +1,4 @@
'\ t
.TH "YAY" "8" "2019\-10\-21" "Yay v9.4+" "Yay Manual"
.TH "YAY" "8" "2019\-10\-21" "Yay v12.0+" "Yay Manual"
.nh
.ad l
.SH NAME
@ -20,10 +19,15 @@ This manpage only covers options unique to Yay. For other options see
\fBpacman(8)\fR.
.SH YAY OPERATIONS
.TP
.B \-Y, \-\-yay
Perform yay specific operations. This is the default if no other operation is
selected.
selected and targets are defined.
.TP
.B \-B, \-\-build
Build a PKGBUILD in a given directory.
.TP
.B \-P, \-\-show
@ -31,13 +35,16 @@ Perform yay specific print operations.
.TP
.B \-G, \-\-getpkgbuild
Downloads PKGBUILD from ABS or AUR. The ABS can only be used for Arch Linux
repositories
Downloads PKGBUILD from ABS or AUR. The ABS can only be used for Arch Linux repositories.
.TP
.B \-W, \-\-web
Web related operations such as voting for AUR packages.
.RE
If no arguments are provided 'yay \-Syu' will be performed.
If no operation is specified 'yay \-Syu' will be performed
If no operation is selected \-Y will be assumed.
If no operation is specified and targets are provided \-Y will be assumed
.SH EXTENDED PACMAN OPERATIONS
.TP
@ -56,7 +63,7 @@ Yay will also remove cached data about devel packages.
.SH NEW OPTIONS
.TP
.B \-\-repo
.B \-N, \-\-repo
Assume all targets are from the repositories. Additionally Actions such as
sysupgrade will only act on repository packages.
@ -75,6 +82,10 @@ packages.
Displays a list of packages matching the search terms and prompts the user on
which packages to install (yogurt mode).
The first search term is used to query the different sources and
the following search terms are used to narrow the search results
through exact matching.
.TP
.B \-\-gendb
Generate development package database. Tracks the latest commit for each
@ -86,16 +97,16 @@ used when migrating to Yay from another AUR helper.
.B \-c, \-\-clean
Remove unneeded dependencies.
.SH SHOW OPTIONS (APPLY TO \-P AND \-\-SHOW)
.TP
.B \-cc
Remove unneeded dependencies, including packages optionally required by any other package.
.SH SHOW OPTIONS (APPLY TO \-P AND \-\-show)
.TP
.B \-c, \-\-complete
Print a list of all AUR and repo packages. This allows shell completion
and is not intended to be used directly by the user.
.TP
.B \-f, \-\-fish
During complete adjust the output for the fish shell.
.TP
.B \-d, \-\-defaultconfig
Print default yay configuration.
@ -104,20 +115,12 @@ Print default yay configuration.
.B \-g, \-\-currentconfig
Print current yay configuration.
.TP
.B \-n, \-\-numberupgrades
Deprecated, use \fByay -Qu\fR and \fBwc -l\fR instead\%.
.TP
.B \-s, \-\-stats
Displays information about installed packages and system health. If there are
orphaned, or out\-of\-date packages, or packages that no longer exist on the
AUR; warnings will be displayed.
.TP
.B \-u, \-\-upgrades
Deprecated, use \fByay -Qu\fR instead\%.
.TP
.B \-w, \-\-news
Print new news from the Archlinux homepage. News is considered new if it is
@ -128,12 +131,35 @@ available news.
.B \-q, \-\-quiet
Only show titles when printing news.
.SH GETPKGBUILD OPTIONS (APPLY TO \-G AND \-\-GETPKGBUILD)
.SH BUILD OPTIONS (APPLY TO \-B AND \-\-build)
.TP
.B \-i, \-\-install
Build and install a PKGBUILD in a given directory
.SH GETPKGBUILD OPTIONS (APPLY TO \-G AND \-\-getpkgbuild)
.TP
.B \-f, \-\-force
Force download for ABS packages that already exist in the current directory. This
ensures directories are not accidentally overwritten.
.TP
.B \-p, \-\-print
Prints the PKGBUILD of the given packages to stdout.
.SH WEB OPTIONS (APPLY TO \-W AND \-\-web)
.TP
Web related operations such as voting for AUR packages.
Requires setting AUR_USERNAME and AUR_PASSWORD environment variables.
.TP
.B \-u, \-\-unvote
Remove vote from AUR package(s)
.TP
.B \-v, \-\-vote
Vote for AUR package(s)
.SH PERMANENT CONFIGURATION SETTINGS
.TP
.B \-\-save
@ -143,23 +169,21 @@ file.
.TP
.B \-\-aururl
Set an alternative AUR URL. This is mostly useful for users in China who wish
to use https://aur.tuna.tsinghua.edu.cn/.
Set an alternative AUR URL.
.TP
.B \-\-aurrpcurl
Set an alternative URL for the AUR /rpc endpoint.
.TP
.B \-\-builddir <dir>
Directory to use for Building AUR Packages. This directory is also used as
the AUR cache when deciding if Yay should skip builds.
.TP
.B \-\-absdir <dir>
Directory used to store downloads from the ABS. During \-G, the PKGBUILD
placed in the current dir symlinks to absdir.
.TP
.B \-\-editor <command>
Editor to use when editing PKGBUILDs. If this is not set the \fBEDITOR\fR
environment variable will be checked, followed by \fBVISUAL\fR. If none of
Editor to use when editing PKGBUILDs. If this is not set the \fBVISUAL\fR
environment variable will be checked, followed by \fBEDITOR\fR. If none of
these are set Yay will prompt the user for an editor.
.TP
@ -225,7 +249,7 @@ cache to never be refreshed.
Sort AUR results by a specific field during search.
.TP
.B \-\-searchby <name|name-desc|maintainer|depends|checkdepends|makedepends|optdepends>
.B \-\-searchby <name|name-desc|maintainer|depends|checkdepends|makedepends|optdepends|provides|conflicts|replaces|groups|keywords|comaintainers>
Search for AUR packages by querying the specified field.
.TP
@ -273,6 +297,9 @@ Unset the answer for the upgrade menu.
Show the clean menu. This menu gives you the chance to fully delete the
downloaded build files from Yay's cache before redownloading a fresh copy.
If 'cleanmenu' is enabled in the configuration file, you can temporarily disable it by
using '--cleanmenu=false' on the command line
.TP
.B \-\-diffmenu
Show the diff menu. This menu gives you the option to view diffs from
@ -290,37 +317,14 @@ 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.
.TP
.B \-\-upgrademenu
Show a detailed list of updates in a similar format to VerbosePkgLists.
Upgrades can also be skipped using numbers, number ranges or repo names.
Additionally ^ can be used to invert the selection.
\fBWarning\fR: It is not recommended to skip updates from the repositories 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.
.TP
.B \-\-nocleanmenu
Do not show the clean menu.
.TP
.B \-\-nodiffmenu
Do not show the diff menu.
.TP
.B \-\-noeditmenu
Do not show the edit menu.
.TP
.B \-\-noupgrademenu
Do not show the upgrade menu.
.TP
.B \-\-askremovemake
Ask to remove makedepends after installing packages.
.TP
.B \-\-askyesremovemake
Ask to remove makedepends after installing packages(with "Y" as default).
.TP
.B \-\-removemake
Remove makedepends after installing packages.
@ -337,6 +341,16 @@ Display repository packages first and then AUR packages.
.B \-\-bottomup
Show AUR packages first and then repository packages.
.TP
.B \-\-singlelineresults
Override pacman's usual double-line search result format and list each result
on its own line.
.TP
.B \-\-doublelineresults
Follow pacman's double-line search result format and list each result using
two lines.
.TP
.B \-\-devel
During sysupgrade also check AUR development packages for updates. Currently
@ -349,9 +363,8 @@ checked almost instantly and not require the original pkgbuild to be downloaded.
The slower pacaur-like devel checks can be implemented manually by piping
a list of packages into yay (see \fBexamples\fR).
.TP
.B \-\-nodevel
Do not check for development packages updates during sysupgrade.
If 'devel' is enabled in the configuration file, you can temporarily disable it by
using '--devel=false' on the command line
.TP
.B \-\-cleanafter
@ -362,8 +375,8 @@ This allows VCS packages to easily pull an update
instead of having to reclone the entire repo.
.TP
.B \-\-nocleanafter
Do not remove package sources after successful Install.
.B \-\-keepsrc
Keep pkg/ and src/ after building packages
.TP
.B \-\-timeupdate
@ -371,8 +384,8 @@ During sysupgrade also compare the build time of installed packages against
the last modification time of each package's AUR page.
.TP
.B \-\-notimeupdate
Do not consider build times during sysupgrade.
.B \-\-separatesources
Separate query results by source, AUR and sync
.TP
.B \-\-redownload
@ -394,23 +407,11 @@ 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.
.TP
.B \-\-noprovides
Do not look for matching providers when searching for AUR packages.
Yay will never show its provider menu but Pacman will still show its
provider menu for repo packages.
.TP
.B \-\-pgpfetch
Prompt to import unknown PGP keys from the \fBvalidpgpkeys\fR field of each
PKGBUILD.
.TP
.B \-\-nopgpfetch
Do not prompt to import unknown PGP keys. This is likely to cause a build
failure unless using options such as \fB\-\-skippgpcheck\fR or a customized
gpg config\%.
.TP
.B \-\-useask
Use pacman's --ask flag to automatically confirm package conflicts. Yay lists
@ -418,11 +419,6 @@ conflicts ahead of time. It is possible that Yay does not detect
a conflict, causing a package to be removed without the user's confirmation.
However, this is very unlikely.
.TP
.B \-\-nouseask
Manually resolve package conflicts during the install. Packages which do not
conflict will not need to be confined manually.
.TP
.B \-\-combinedupgrade
During sysupgrade, Yay will first perform a refresh, then show
@ -434,12 +430,6 @@ If Yay exits for any reason After the refresh without upgrading. It is then
the user's responsibility to either resolve the reason Yay exited or run
a sysupgrade through pacman directly.
.TP
.B \-\-nocombinedupgrade
During sysupgrade, Pacman \-Syu will be called, then the AUR upgrade will
start. This means the upgrade menu and pkgbuild review will be performed
after the sysupgrade has finished.
.TP
.B \-\-batchinstall
When building and installing AUR packages instead of installing each package
@ -447,10 +437,6 @@ after building, queue each package for install. Then once either all packages
are built or a package in the build queue is needed as a dependency to build
another package, install all the packages in the install queue.
.TP
.B \-\-nobatchinstall
Always install AUR packages immediately after building them.
.TP
.B \-\-rebuild
Always build target packages even when a copy is available in cache.
@ -504,10 +490,6 @@ separated list that is quoted by the shell.
Loop sudo calls in the background to prevent sudo from timing out during long
builds.
.TP
.B \-\-nosudoloop
Do not loop sudo calls in the background.
.SH EXAMPLES
.TP
yay \fIfoo\fR
@ -612,6 +594,6 @@ See the arch wiki at https://wiki.archlinux.org/index.php/Arch_User_Repository f
Please report bugs to our GitHub page https://github.com/Jguer/yay
.SH AUTHORS
Jguer <joaogg3@gmail.com>
Jguer <joguer@proton.me>
.br
Anna <morganamilo@gmail.com>
Morgan <morganamilo@archlinux.org>

View File

@ -1,315 +0,0 @@
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
alpm "github.com/Jguer/go-alpm"
"github.com/leonelquinteros/gotext"
"github.com/pkg/errors"
"github.com/Jguer/yay/v10/pkg/multierror"
"github.com/Jguer/yay/v10/pkg/text"
)
const gitDiffRefName = "AUR_SEEN"
// Update the YAY_DIFF_REVIEW ref to HEAD. We use this ref to determine which diff were
// reviewed by the user
func gitUpdateSeenRef(path, name string) error {
_, stderr, err := capture(passToGit(filepath.Join(path, name), "update-ref", gitDiffRefName, "HEAD"))
if err != nil {
return fmt.Errorf("%s %s", stderr, err)
}
return nil
}
// Return wether or not we have reviewed a diff yet. It checks for the existence of
// YAY_DIFF_REVIEW in the git ref-list
func gitHasLastSeenRef(path, name string) bool {
_, _, err := capture(passToGit(filepath.Join(path, name), "rev-parse", "--quiet", "--verify", gitDiffRefName))
return err == nil
}
// Returns the last reviewed hash. If YAY_DIFF_REVIEW exists it will return this hash.
// If it does not it will return empty tree as no diff have been reviewed yet.
func getLastSeenHash(path, name string) (string, error) {
if gitHasLastSeenRef(path, name) {
stdout, stderr, err := capture(passToGit(filepath.Join(path, name), "rev-parse", gitDiffRefName))
if err != nil {
return "", fmt.Errorf("%s %s", stderr, err)
}
lines := strings.Split(stdout, "\n")
return lines[0], nil
}
return gitEmptyTree, nil
}
// Check whether or not a diff exists between the last reviewed diff and
// HEAD@{upstream}
func gitHasDiff(path, name string) (bool, error) {
if gitHasLastSeenRef(path, name) {
stdout, stderr, err := capture(passToGit(filepath.Join(path, name), "rev-parse", gitDiffRefName, "HEAD@{upstream}"))
if err != nil {
return false, fmt.Errorf("%s%s", stderr, err)
}
lines := strings.Split(stdout, "\n")
lastseen := lines[0]
upstream := lines[1]
return lastseen != upstream, nil
}
// If YAY_DIFF_REVIEW does not exists, we have never reviewed a diff for this package
// and should display it.
return true, nil
}
// TODO: yay-next passes args through the header, use that to unify ABS and AUR
func gitDownloadABS(url, path, name string) (bool, error) {
if err := os.MkdirAll(path, 0755); err != nil {
return false, err
}
if _, errExist := os.Stat(filepath.Join(path, name)); os.IsNotExist(errExist) {
cmd := passToGit(path, "clone", "--no-progress", "--single-branch",
"-b", "packages/"+name, url, name)
cmd.Env = append(os.Environ(), "GIT_TERMINAL_PROMPT=0")
_, stderr, err := capture(cmd)
if err != nil {
return false, fmt.Errorf(gotext.Get("error cloning %s: %s", name, stderr))
}
return true, nil
} else if errExist != nil {
return false, fmt.Errorf(gotext.Get("error reading %s", filepath.Join(path, name, ".git")))
}
cmd := passToGit(filepath.Join(path, name), "pull", "--ff-only")
cmd.Env = append(os.Environ(), "GIT_TERMINAL_PROMPT=0")
_, stderr, err := capture(cmd)
if err != nil {
return false, fmt.Errorf(gotext.Get("error fetching %s: %s", name, stderr))
}
return true, nil
}
func gitDownload(url, path, name string) (bool, error) {
_, err := os.Stat(filepath.Join(path, name, ".git"))
if os.IsNotExist(err) {
cmd := passToGit(path, "clone", "--no-progress", url, name)
cmd.Env = append(os.Environ(), "GIT_TERMINAL_PROMPT=0")
_, stderr, errCapture := capture(cmd)
if errCapture != nil {
return false, fmt.Errorf(gotext.Get("error cloning %s: %s", name, stderr))
}
return true, nil
} else if err != nil {
return false, fmt.Errorf(gotext.Get("error reading %s", filepath.Join(path, name, ".git")))
}
cmd := passToGit(filepath.Join(path, name), "fetch")
cmd.Env = append(os.Environ(), "GIT_TERMINAL_PROMPT=0")
_, stderr, err := capture(cmd)
if err != nil {
return false, fmt.Errorf(gotext.Get("error fetching %s: %s", name, stderr))
}
return false, nil
}
func gitMerge(path, name string) error {
_, stderr, err := capture(passToGit(filepath.Join(path, name), "reset", "--hard", "HEAD"))
if err != nil {
return fmt.Errorf(gotext.Get("error resetting %s: %s", name, stderr))
}
_, stderr, err = capture(passToGit(filepath.Join(path, name), "merge", "--no-edit", "--ff"))
if err != nil {
return fmt.Errorf(gotext.Get("error merging %s: %s", name, stderr))
}
return nil
}
func getPkgbuilds(pkgs []string) error {
missing := false
wd, err := os.Getwd()
if err != nil {
return err
}
pkgs = removeInvalidTargets(pkgs)
aur, repo, err := packageSlices(pkgs)
if err != nil {
return err
}
for n := range aur {
_, pkg := splitDBFromName(aur[n])
aur[n] = pkg
}
info, err := aurInfoPrint(aur)
if err != nil {
return err
}
if len(repo) > 0 {
missing, err = getPkgbuildsfromABS(repo, wd)
if err != nil {
return err
}
}
if len(aur) > 0 {
allBases := getBases(info)
bases := make([]Base, 0)
for _, base := range allBases {
name := base.Pkgbase()
_, err = os.Stat(filepath.Join(wd, name))
switch {
case err != nil && !os.IsNotExist(err):
text.Errorln(err)
continue
default:
if err = os.RemoveAll(filepath.Join(wd, name)); err != nil {
text.Errorln(err)
continue
}
}
bases = append(bases, base)
}
if _, err = downloadPkgbuilds(bases, nil, wd); err != nil {
return err
}
missing = missing || len(aur) != len(info)
}
if missing {
err = fmt.Errorf("")
}
return err
}
// GetPkgbuild downloads pkgbuild from the ABS.
func getPkgbuildsfromABS(pkgs []string, path string) (bool, error) {
var wg sync.WaitGroup
var mux sync.Mutex
var errs multierror.MultiError
names := make(map[string]string)
missing := make([]string, 0)
downloaded := 0
dbList, err := alpmHandle.SyncDBs()
if err != nil {
return false, err
}
for _, pkgN := range pkgs {
var pkg *alpm.Package
var err error
var url string
pkgDB, name := splitDBFromName(pkgN)
if pkgDB != "" {
if db, errSync := alpmHandle.SyncDBByName(pkgDB); errSync == nil {
pkg = db.Pkg(name)
}
} else {
_ = dbList.ForEach(func(db alpm.DB) error {
if pkg = db.Pkg(name); pkg != nil {
return fmt.Errorf("")
}
return nil
})
}
if pkg == nil {
missing = append(missing, name)
continue
}
name = pkg.Base()
if name == "" {
name = pkg.Name()
}
// TODO: Check existence with ls-remote
// https://git.archlinux.org/svntogit/packages.git
switch pkg.DB().Name() {
case "core", "extra", "testing":
url = "https://git.archlinux.org/svntogit/packages.git"
case "community", "multilib", "community-testing", "multilib-testing":
url = "https://git.archlinux.org/svntogit/community.git"
default:
missing = append(missing, name)
continue
}
_, err = os.Stat(filepath.Join(path, name))
switch {
case err != nil && !os.IsNotExist(err):
fmt.Fprintln(os.Stderr, bold(red(smallArrow)), err)
continue
case os.IsNotExist(err), cmdArgs.existsArg("f", "force"):
if err = os.RemoveAll(filepath.Join(path, name)); err != nil {
fmt.Fprintln(os.Stderr, bold(red(smallArrow)), err)
continue
}
default:
text.Warn(gotext.Get("%s already downloaded -- use -f to overwrite", cyan(name)))
continue
}
names[name] = url
}
if len(missing) != 0 {
text.Warnln(gotext.Get("Missing ABS packages:"),
cyan(strings.Join(missing, ", ")))
}
download := func(pkg string, url string) {
defer wg.Done()
if _, err := gitDownloadABS(url, config.ABSDir, pkg); err != nil {
errs.Add(errors.New(gotext.Get("failed to get pkgbuild: %s: %s", cyan(pkg), err.Error())))
return
}
_, stderr, err := capture(exec.Command("cp", "-r", filepath.Join(config.ABSDir, pkg, "trunk"), filepath.Join(path, pkg)))
mux.Lock()
downloaded++
if err != nil {
errs.Add(errors.New(gotext.Get("failed to link %s: %s", cyan(pkg), stderr)))
} else {
fmt.Fprintln(os.Stdout, gotext.Get("(%d/%d) Downloaded PKGBUILD from ABS: %s", downloaded, len(names), cyan(pkg)))
}
mux.Unlock()
}
count := 0
for name, url := range names {
wg.Add(1)
go download(name, url)
count++
if count%25 == 0 {
wg.Wait()
}
}
wg.Wait()
return len(missing) != 0, errs.Return()
}

9
errors.go Normal file
View File

@ -0,0 +1,9 @@
package main
import (
"errors"
"github.com/leonelquinteros/gotext"
)
var ErrPackagesNotFound = errors.New(gotext.Get("could not find all required packages"))

133
exec.go
View File

@ -1,133 +0,0 @@
package main
import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/leonelquinteros/gotext"
"golang.org/x/crypto/ssh/terminal"
"github.com/Jguer/yay/v10/pkg/text"
)
func show(cmd *exec.Cmd) error {
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
err := cmd.Run()
if err != nil {
return fmt.Errorf("")
}
return nil
}
func capture(cmd *exec.Cmd) (stdout, stderr string, err error) {
var outbuf, errbuf bytes.Buffer
cmd.Stdout = &outbuf
cmd.Stderr = &errbuf
err = cmd.Run()
stdout = strings.TrimSpace(outbuf.String())
stderr = strings.TrimSpace(errbuf.String())
return stdout, stderr, err
}
func sudoLoopBackground() {
updateSudo()
go sudoLoop()
}
func sudoLoop() {
for {
updateSudo()
time.Sleep(298 * time.Second)
}
}
func updateSudo() {
for {
mSudoFlags := strings.Fields(config.SudoFlags)
mSudoFlags = append([]string{"-v"}, mSudoFlags...)
err := show(exec.Command(config.SudoBin, mSudoFlags...))
if err != nil {
fmt.Fprintln(os.Stderr, err)
} else {
break
}
}
}
// waitLock will lock yay checking the status of db.lck until it does not exist
func waitLock() {
if _, err := os.Stat(filepath.Join(pacmanConf.DBPath, "db.lck")); err != nil {
return
}
text.Warnln(gotext.Get("%s is present.", filepath.Join(pacmanConf.DBPath, "db.lck")))
text.Warn(gotext.Get("There may be another Pacman instance running. Waiting..."))
for {
time.Sleep(3 * time.Second)
if _, err := os.Stat(filepath.Join(pacmanConf.DBPath, "db.lck")); err != nil {
fmt.Println()
return
}
}
}
func passToPacman(args *arguments) *exec.Cmd {
argArr := make([]string, 0)
mSudoFlags := strings.Fields(config.SudoFlags)
if args.needRoot() {
argArr = append(argArr, config.SudoBin)
argArr = append(argArr, mSudoFlags...)
}
argArr = append(argArr, config.PacmanBin)
argArr = append(argArr, cmdArgs.formatGlobals()...)
argArr = append(argArr, args.formatArgs()...)
if config.NoConfirm {
argArr = append(argArr, "--noconfirm")
}
argArr = append(argArr, "--config", config.PacmanConf, "--")
argArr = append(argArr, args.targets...)
if args.needRoot() {
waitLock()
}
return exec.Command(argArr[0], argArr[1:]...)
}
func passToMakepkg(dir string, args ...string) *exec.Cmd {
mflags := strings.Fields(config.MFlags)
args = append(args, mflags...)
if config.MakepkgConf != "" {
args = append(args, "--config", config.MakepkgConf)
}
cmd := exec.Command(config.MakepkgBin, args...)
cmd.Dir = dir
return cmd
}
func passToGit(dir string, _args ...string) *exec.Cmd {
gitflags := strings.Fields(config.GitFlags)
args := []string{"-C", dir}
args = append(args, gitflags...)
args = append(args, _args...)
cmd := exec.Command(config.GitBin, args...)
return cmd
}
func isTty() bool {
return terminal.IsTerminal(int(os.Stdout.Fd()))
}

80
get.go Normal file
View File

@ -0,0 +1,80 @@
package main
import (
"context"
"fmt"
"net/http"
"os"
"strings"
"github.com/Jguer/aur"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/download"
"github.com/Jguer/yay/v12/pkg/runtime"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
)
// yay -Gp.
func printPkgbuilds(dbExecutor download.DBSearcher, aurClient aur.QueryClient,
httpClient *http.Client, logger *text.Logger, targets []string,
mode parser.TargetMode, aurURL string,
) error {
pkgbuilds, err := download.PKGBUILDs(dbExecutor, aurClient, httpClient, logger, targets, aurURL, mode)
if err != nil {
logger.Errorln(err)
}
for target, pkgbuild := range pkgbuilds {
logger.Printf("\n\n# %s\n\n%s", target, string(pkgbuild))
}
if len(pkgbuilds) != len(targets) {
missing := []string{}
for _, target := range targets {
if _, ok := pkgbuilds[target]; !ok {
missing = append(missing, target)
}
}
logger.Warnln(gotext.Get("Unable to find the following packages:"), " ", strings.Join(missing, ", "))
return fmt.Errorf("")
}
return nil
}
// yay -G.
func getPkgbuilds(ctx context.Context, dbExecutor download.DBSearcher, aurClient aur.QueryClient,
run *runtime.Runtime, targets []string, force bool,
) error {
wd, err := os.Getwd()
if err != nil {
return err
}
cloned, errD := download.PKGBUILDRepos(ctx, dbExecutor, aurClient,
run.CmdBuilder, run.Logger, targets, run.Cfg.Mode, run.Cfg.AURURL, wd, force)
if errD != nil {
run.Logger.Errorln(errD)
}
if len(targets) != len(cloned) {
missing := []string{}
for _, target := range targets {
if _, ok := cloned[target]; !ok {
missing = append(missing, target)
}
}
run.Logger.Warnln(gotext.Get("Unable to find the following packages:"), " ", strings.Join(missing, ", "))
err = fmt.Errorf("")
}
return err
}

39
go.mod
View File

@ -1,14 +1,35 @@
module github.com/Jguer/yay/v10
module github.com/Jguer/yay/v12
require (
github.com/Jguer/go-alpm v0.0.0-20200405152916-a3feea4322e9
github.com/Morganamilo/go-pacmanconf v0.0.0-20180910220353-9c5265e1b14f
github.com/Jguer/aur v1.2.3
github.com/Jguer/go-alpm/v2 v2.2.2
github.com/Jguer/votar v1.0.0
github.com/Morganamilo/go-pacmanconf v0.0.0-20210502114700-cff030e927a5
github.com/Morganamilo/go-srcinfo v1.0.0
github.com/leonelquinteros/gotext v1.4.0
github.com/mikkeloscar/aur v0.0.0-20200113170522-1cb4e2949656
github.com/pkg/errors v0.9.1
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9
golang.org/x/sys v0.0.0-20200610111108-226ff32320da // indirect
github.com/adrg/strutil v0.3.1
github.com/bradleyjkemp/cupaloy v2.3.0+incompatible
github.com/deckarep/golang-set/v2 v2.8.0
github.com/hashicorp/go-multierror v1.1.1
github.com/leonelquinteros/gotext v1.7.2
github.com/stretchr/testify v1.10.0
golang.org/x/net v0.41.0
golang.org/x/sys v0.33.0
golang.org/x/term v0.32.0
gopkg.in/h2non/gock.v1 v1.1.2
)
go 1.14
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/itchyny/gojq v0.12.17 // indirect
github.com/itchyny/timefmt-go v0.1.6 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/ohler55/ojg v1.26.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
go 1.23.5
toolchain go1.24.0

86
go.sum
View File

@ -1,22 +1,68 @@
github.com/Jguer/go-alpm v0.0.0-20200405152916-a3feea4322e9 h1:lLQSUe6iRdtFrP0zkDV7n8I8XKSxRHQTEU1KRh4IOLg=
github.com/Jguer/go-alpm v0.0.0-20200405152916-a3feea4322e9/go.mod h1:D5SUcIS9Yiz/L8cjRzq/992eERnx6ugYmGlc4e7xdus=
github.com/Morganamilo/go-pacmanconf v0.0.0-20180910220353-9c5265e1b14f h1:ptFKynTV1p8JCzqk81NcMj0DV0Xle+PdKxfHjPbdIOU=
github.com/Morganamilo/go-pacmanconf v0.0.0-20180910220353-9c5265e1b14f/go.mod h1:Hk55m330jNiwxRodIlMCvw5iEyoRUCIY64W1p9D+tHc=
github.com/Jguer/aur v1.2.3 h1:D+OGgLxnAnZnw88DsRvnRQsn0Poxsy9ng7pBcsA0krM=
github.com/Jguer/aur v1.2.3/go.mod h1:Dahvb6L1yr0rR7svyYSDwaRJoQMeyvJblwJ3QH/7CUs=
github.com/Jguer/go-alpm/v2 v2.2.2 h1:sPwUoZp1X5Tw6K6Ba1lWvVJfcgVNEGVcxARLBttZnC0=
github.com/Jguer/go-alpm/v2 v2.2.2/go.mod h1:lfe8gSe83F/KERaQvEfrSqQ4n+8bES+ZIyKWR/gm3MI=
github.com/Jguer/votar v1.0.0 h1:drPYpV5Py5BeAQS8xezmT6uCEfLzotNjLf5yfmlHKTg=
github.com/Jguer/votar v1.0.0/go.mod h1:rc6vgVlTqNjI4nAnPbDTbdxw/N7kXkbB8BcUDjeFbYQ=
github.com/Morganamilo/go-pacmanconf v0.0.0-20210502114700-cff030e927a5 h1:TMscPjkb1ThXN32LuFY5bEYIcXZx3YlwzhS1GxNpn/c=
github.com/Morganamilo/go-pacmanconf v0.0.0-20210502114700-cff030e927a5/go.mod h1:Hk55m330jNiwxRodIlMCvw5iEyoRUCIY64W1p9D+tHc=
github.com/Morganamilo/go-srcinfo v1.0.0 h1:Wh4nEF+HJWo+29hnxM18Q2hi+DUf0GejS13+Wg+dzmI=
github.com/Morganamilo/go-srcinfo v1.0.0/go.mod h1:MP6VGY1NNpVUmYIEgoM9acix95KQqIRyqQ0hCLsyYUY=
github.com/leonelquinteros/gotext v1.4.0 h1:2NHPCto5IoMXbrT0bldPrxj0qM5asOCwtb1aUQZ1tys=
github.com/leonelquinteros/gotext v1.4.0/go.mod h1:yZGXREmoGTtBvZHNcc+Yfug49G/2spuF/i/Qlsvz1Us=
github.com/mikkeloscar/aur v0.0.0-20200113170522-1cb4e2949656 h1:j679+jxcDkCFblYk+I+G71HQTFxM3PacYbVCiYmhRhU=
github.com/mikkeloscar/aur v0.0.0-20200113170522-1cb4e2949656/go.mod h1:nYOKcK8tIj69ZZ8uDOWoiT+L25NvlOQaraDqTec/idA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200610111108-226ff32320da h1:bGb80FudwxpeucJUjPYJXuJ8Hk91vNtfvrymzwiei38=
golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
github.com/adrg/strutil v0.3.1 h1:OLvSS7CSJO8lBii4YmBt8jiK9QOtB9CzCzwl4Ic/Fz4=
github.com/adrg/strutil v0.3.1/go.mod h1:8h90y18QLrs11IBffcGX3NW/GFBXCMcNg4M7H6MspPA=
github.com/alexflint/go-arg v1.4.3/go.mod h1:3PZ/wp/8HuqRZMUUgu7I+e1qcpUbvmS258mRXkFH4IA=
github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
github.com/bradleyjkemp/cupaloy v2.3.0+incompatible h1:UafIjBvWQmS9i/xRg+CamMrnLTKNzo+bdmT/oH34c2Y=
github.com/bradleyjkemp/cupaloy v2.3.0+incompatible/go.mod h1:Au1Xw1sgaJ5iSFktEhYsS0dbQiS1B0/XMXl+42y9Ilk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ=
github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/itchyny/gojq v0.12.17 h1:8av8eGduDb5+rvEdaOO+zQUjA04MS0m3Ps8HiD+fceg=
github.com/itchyny/gojq v0.12.17/go.mod h1:WBrEMkgAfAGO1LUcGOckBl5O726KPp+OlkKug0I/FEY=
github.com/itchyny/timefmt-go v0.1.6 h1:ia3s54iciXDdzWzwaVKXZPbiXzxxnv1SPGFfM/myJ5Q=
github.com/itchyny/timefmt-go v0.1.6/go.mod h1:RRDZYC5s9ErkjQvTvvU7keJjxUYzIISJGxm9/mAERQg=
github.com/leonelquinteros/gotext v1.7.2 h1:bDPndU8nt+/kRo1m4l/1OXiiy2v7Z7dfPQ9+YP7G1Mc=
github.com/leonelquinteros/gotext v1.7.2/go.mod h1:9/haCkm5P7Jay1sxKDGJ5WIg4zkz8oZKw4ekNpALob8=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/ohler55/ojg v1.26.1 h1:J5TaLmVEuvnpVH7JMdT1QdbpJU545Yp6cKiCO4aQILc=
github.com/ohler55/ojg v1.26.1/go.mod h1:gQhDVpQLqrmnd2eqGAvJtn+NfKoYJbe/A4Sj3/Vro4o=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

1167
install.go

File diff suppressed because it is too large Load Diff

View File

@ -1,241 +0,0 @@
package main
import (
"bytes"
"context"
"fmt"
"io/ioutil"
"net/http"
"os"
"path"
"regexp"
"testing"
gosrc "github.com/Morganamilo/go-srcinfo"
rpc "github.com/mikkeloscar/aur"
)
const (
// The default port used by the PGP key server.
gpgServerPort = 11371
)
func init() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
regex := regexp.MustCompile(`search=0[xX]([a-fA-F0-9]+)`)
matches := regex.FindStringSubmatch(r.RequestURI)
data := ""
if matches != nil {
data = getPgpKey(matches[1])
}
w.Header().Set("Content-Type", "application/pgp-keys")
_, err := w.Write([]byte(data))
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
})
}
func newPkg(basename string) *rpc.Pkg {
return &rpc.Pkg{Name: basename, PackageBase: basename}
}
func getPgpKey(key string) string {
var buffer bytes.Buffer
if contents, err := ioutil.ReadFile(path.Join("testdata", "keys", key)); err == nil {
buffer.WriteString("-----BEGIN PGP PUBLIC KEY BLOCK-----\n")
buffer.WriteString("Version: SKS 1.1.6\n")
buffer.WriteString("Comment: Hostname: yay\n\n")
buffer.Write(contents)
buffer.WriteString("\n-----END PGP PUBLIC KEY BLOCK-----\n")
}
return buffer.String()
}
func startPgpKeyServer() *http.Server {
srv := &http.Server{Addr: fmt.Sprintf("127.0.0.1:%d", gpgServerPort)}
go func() {
err := srv.ListenAndServe()
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
}()
return srv
}
func TestImportKeys(t *testing.T) {
keyringDir, err := ioutil.TempDir("/tmp", "yay-test-keyring")
if err != nil {
t.Fatalf("Unable to init test keyring %q: %v\n", keyringDir, err)
}
defer os.RemoveAll(keyringDir)
config.GpgBin = "gpg"
config.GpgFlags = fmt.Sprintf("--homedir %s --keyserver 127.0.0.1", keyringDir)
server := startPgpKeyServer()
defer func() {
err := server.Shutdown(context.TODO())
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
}()
casetests := []struct {
keys []string
wantError bool
}{
// Single key, should succeed.
// C52048C0C0748FEE227D47A2702353E0F7E48EDB: Thomas Dickey.
{
keys: []string{"C52048C0C0748FEE227D47A2702353E0F7E48EDB"},
wantError: false,
},
// Two keys, should succeed as well.
// 11E521D646982372EB577A1F8F0871F202119294: Tom Stellard.
// B6C8F98282B944E3B0D5C2530FC3042E345AD05D: Hans Wennborg.
{
keys: []string{"11E521D646982372EB577A1F8F0871F202119294",
"B6C8F98282B944E3B0D5C2530FC3042E345AD05D"},
wantError: false,
},
// Single invalid key, should fail.
{
keys: []string{"THIS-SHOULD-FAIL"},
wantError: true,
},
// Two invalid keys, should fail.
{
keys: []string{"THIS-SHOULD-FAIL", "THIS-ONE-SHOULD-FAIL-TOO"},
wantError: true,
},
// Invalid + valid key. Should fail as well.
// 647F28654894E3BD457199BE38DBBDC86092693E: Greg Kroah-Hartman.
{
keys: []string{"THIS-SHOULD-FAIL",
"647F28654894E3BD457199BE38DBBDC86092693E"},
wantError: true,
},
}
for _, tt := range casetests {
err := importKeys(tt.keys)
if !tt.wantError {
if err != nil {
t.Fatalf("Got error %q, want no error", err)
}
continue
}
// Here, we want to see the error.
if err == nil {
t.Fatalf("Got no error; want error")
}
}
}
func makeSrcinfo(pkgbase string, pgpkeys ...string) *gosrc.Srcinfo {
srcinfo := gosrc.Srcinfo{}
srcinfo.Pkgbase = pkgbase
srcinfo.ValidPGPKeys = pgpkeys
return &srcinfo
}
func TestCheckPgpKeys(t *testing.T) {
keyringDir, err := ioutil.TempDir("/tmp", "yay-test-keyring")
if err != nil {
t.Fatalf("Unable to init test keyring: %v\n", err)
}
defer os.RemoveAll(keyringDir)
config.GpgBin = "gpg"
config.GpgFlags = fmt.Sprintf("--homedir %s --keyserver 127.0.0.1", keyringDir)
server := startPgpKeyServer()
defer func() {
err := server.Shutdown(context.TODO())
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
}()
casetests := []struct {
pkgs Base
srcinfos map[string]*gosrc.Srcinfo
wantError bool
}{
// cower: single package, one valid key not yet in the keyring.
// 487EACC08557AD082088DABA1EB2638FF56C0C53: Dave Reisner.
{
pkgs: Base{newPkg("cower")},
srcinfos: map[string]*gosrc.Srcinfo{"cower": makeSrcinfo("cower", "487EACC08557AD082088DABA1EB2638FF56C0C53")},
wantError: false,
},
// libc++: single package, two valid keys not yet in the keyring.
// 11E521D646982372EB577A1F8F0871F202119294: Tom Stellard.
// B6C8F98282B944E3B0D5C2530FC3042E345AD05D: Hans Wennborg.
{
pkgs: Base{newPkg("libc++")},
srcinfos: map[string]*gosrc.Srcinfo{
"libc++": makeSrcinfo("libc++", "11E521D646982372EB577A1F8F0871F202119294", "B6C8F98282B944E3B0D5C2530FC3042E345AD05D")},
wantError: false,
},
// Two dummy packages requiring the same key.
// ABAF11C65A2970B130ABE3C479BE3E4300411886: Linus Torvalds.
{
pkgs: Base{newPkg("dummy-1"), newPkg("dummy-2")},
srcinfos: map[string]*gosrc.Srcinfo{
"dummy-1": makeSrcinfo("dummy-1",
"ABAF11C65A2970B130ABE3C479BE3E4300411886"),
"dummy-2": makeSrcinfo("dummy-2", "ABAF11C65A2970B130ABE3C479BE3E4300411886")},
wantError: false,
},
// dummy package: single package, two valid keys, one of them already
// in the keyring.
// 11E521D646982372EB577A1F8F0871F202119294: Tom Stellard.
// C52048C0C0748FEE227D47A2702353E0F7E48EDB: Thomas Dickey.
{
pkgs: Base{newPkg("dummy-3")},
srcinfos: map[string]*gosrc.Srcinfo{
"dummy-3": makeSrcinfo("dummy-3", "11E521D646982372EB577A1F8F0871F202119294", "C52048C0C0748FEE227D47A2702353E0F7E48EDB")},
wantError: false,
},
// Two dummy packages with existing keys.
{
pkgs: Base{newPkg("dummy-4"), newPkg("dummy-5")},
srcinfos: map[string]*gosrc.Srcinfo{
"dummy-4": makeSrcinfo("dummy-4", "11E521D646982372EB577A1F8F0871F202119294"),
"dummy-5": makeSrcinfo("dummy-5", "C52048C0C0748FEE227D47A2702353E0F7E48EDB")},
wantError: false,
},
// Dummy package with invalid key, should fail.
{
pkgs: Base{newPkg("dummy-7")},
srcinfos: map[string]*gosrc.Srcinfo{"dummy-7": makeSrcinfo("dummy-7", "THIS-SHOULD-FAIL")},
wantError: true,
},
// Dummy package with both an invalid an another valid key, should fail.
// A314827C4E4250A204CE6E13284FC34C8E4B1A25: Thomas Bächler.
{
pkgs: Base{newPkg("dummy-8")},
srcinfos: map[string]*gosrc.Srcinfo{"dummy-8": makeSrcinfo("dummy-8", "A314827C4E4250A204CE6E13284FC34C8E4B1A25", "THIS-SHOULD-FAIL")},
wantError: true,
},
}
for _, tt := range casetests {
err := checkPgpKeys([]Base{tt.pkgs}, tt.srcinfos)
if !tt.wantError {
if err != nil {
t.Fatalf("Got error %q, want no error", err)
}
continue
}
// Here, we want to see the error.
if err == nil {
t.Fatalf("Got no error; want error")
}
}
}

108
local_install.go Normal file
View File

@ -0,0 +1,108 @@
// Experimental code for install local with dependency refactoring
// Not at feature parity with install.go
package main
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/dep"
"github.com/Jguer/yay/v12/pkg/multierror"
"github.com/Jguer/yay/v12/pkg/runtime"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/sync"
gosrc "github.com/Morganamilo/go-srcinfo"
"github.com/leonelquinteros/gotext"
)
var ErrNoBuildFiles = errors.New(gotext.Get("cannot find PKGBUILD and .SRCINFO in directory"))
func srcinfoExists(ctx context.Context,
cmdBuilder exe.ICmdBuilder, targetDir string,
) error {
srcInfoDir := filepath.Join(targetDir, ".SRCINFO")
pkgbuildDir := filepath.Join(targetDir, "PKGBUILD")
if _, err := os.Stat(srcInfoDir); err == nil {
if _, err := os.Stat(pkgbuildDir); err == nil {
return nil
}
}
if _, err := os.Stat(pkgbuildDir); err == nil {
// run makepkg to generate .SRCINFO
srcinfo, stderr, err := cmdBuilder.Capture(cmdBuilder.BuildMakepkgCmd(ctx, targetDir, "--printsrcinfo"))
if err != nil {
return fmt.Errorf("unable to generate .SRCINFO: %w - %s", err, stderr)
}
if srcinfo == "" {
return fmt.Errorf("generated .SRCINFO is empty, check your PKGBUILD for errors")
}
if err := os.WriteFile(srcInfoDir, []byte(srcinfo), 0o600); err != nil {
return fmt.Errorf("unable to write .SRCINFO: %w", err)
}
return nil
}
return fmt.Errorf("%w: %s", ErrNoBuildFiles, targetDir)
}
func installLocalPKGBUILD(
ctx context.Context,
run *runtime.Runtime,
cmdArgs *parser.Arguments,
dbExecutor db.Executor,
) error {
aurCache := run.AURClient
noCheck := strings.Contains(run.Cfg.MFlags, "--nocheck")
if len(cmdArgs.Targets) < 1 {
return errors.New(gotext.Get("no target directories specified"))
}
srcInfos := map[string]*gosrc.Srcinfo{}
for _, targetDir := range cmdArgs.Targets {
if err := srcinfoExists(ctx, run.CmdBuilder, targetDir); err != nil {
return err
}
pkgbuild, err := gosrc.ParseFile(filepath.Join(targetDir, ".SRCINFO"))
if err != nil {
return fmt.Errorf("%s: %w", gotext.Get("failed to parse .SRCINFO"), err)
}
srcInfos[targetDir] = pkgbuild
}
grapher := dep.NewGrapher(dbExecutor, aurCache, false, settings.NoConfirm,
cmdArgs.ExistsDouble("d", "nodeps"), noCheck, cmdArgs.ExistsArg("needed"),
run.Logger.Child("grapher"))
graph, err := grapher.GraphFromSrcInfos(ctx, nil, srcInfos)
if err != nil {
return err
}
opService := sync.NewOperationService(ctx, dbExecutor, run)
multiErr := &multierror.MultiError{}
targets := graph.TopoSortedLayerMap(func(name string, ii *dep.InstallInfo) error {
if ii.Source == dep.Missing {
multiErr.Add(fmt.Errorf("%w: %s %s", ErrPackagesNotFound, name, ii.Version))
}
return nil
})
if err := multiErr.Return(); err != nil {
return err
}
return opService.Run(ctx, run, cmdArgs, targets, []string{})
}

1019
local_install_test.go Normal file

File diff suppressed because it is too large Load Diff

343
main.go
View File

@ -1,245 +1,154 @@
package main // import "github.com/Jguer/yay"
import (
"encoding/json"
"context"
"errors"
"fmt"
"os"
"path/filepath"
"os/exec"
"runtime/debug"
"strings"
alpm "github.com/Jguer/go-alpm"
pacmanconf "github.com/Morganamilo/go-pacmanconf"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v10/pkg/text"
"github.com/Jguer/yay/v12/pkg/db/ialpm"
"github.com/Jguer/yay/v12/pkg/runtime"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
)
func setPaths() error {
if configHome = os.Getenv("XDG_CONFIG_HOME"); configHome != "" {
configHome = filepath.Join(configHome, "yay")
} else if configHome = os.Getenv("HOME"); configHome != "" {
configHome = filepath.Join(configHome, ".config/yay")
} else {
return errors.New(gotext.Get("%s and %s unset", "XDG_CONFIG_HOME", "HOME"))
}
if cacheHome = os.Getenv("XDG_CACHE_HOME"); cacheHome != "" {
cacheHome = filepath.Join(cacheHome, "yay")
} else if cacheHome = os.Getenv("HOME"); cacheHome != "" {
cacheHome = filepath.Join(cacheHome, ".cache/yay")
} else {
return errors.New(gotext.Get("%s and %s unset", "XDG_CACHE_HOME", "HOME"))
}
configFile = filepath.Join(configHome, configFileName)
vcsFile = filepath.Join(cacheHome, vcsFileName)
return nil
}
var (
yayVersion = "12.0.4" // To be set by compiler.
localePath = "/usr/share/locale" // To be set by compiler.
)
func initGotext() {
if envLocalePath := os.Getenv("LOCALE_PATH"); envLocalePath != "" {
localePath = envLocalePath
}
if lc := os.Getenv("LANGUAGE"); lc != "" {
// Split LANGUAGE by ':' and prioritize the first locale
// Should fix in gotext to support this
locales := strings.Split(lc, ":")
if len(locales) > 0 && locales[0] != "" {
gotext.Configure(localePath, locales[0], "yay")
}
} else if lc := os.Getenv("LC_ALL"); lc != "" {
gotext.Configure(localePath, lc, "yay")
} else if lc := os.Getenv("LC_MESSAGES"); lc != "" {
gotext.Configure(localePath, lc, "yay")
} else {
gotext.Configure(localePath, os.Getenv("LANG"), "yay")
}
func initConfig() error {
cfile, err := os.Open(configFile)
if !os.IsNotExist(err) && err != nil {
return errors.New(gotext.Get("failed to open config file '%s': %s", configFile, err))
}
defer cfile.Close()
if !os.IsNotExist(err) {
decoder := json.NewDecoder(cfile)
if err = decoder.Decode(&config); err != nil {
return errors.New(gotext.Get("failed to read config file '%s': %s", configFile, err))
}
}
aurdest := os.Getenv("AURDEST")
if aurdest != "" {
config.BuildDir = aurdest
}
return nil
}
func initVCS() error {
vfile, err := os.Open(vcsFile)
if !os.IsNotExist(err) && err != nil {
return errors.New(gotext.Get("failed to open vcs file '%s': %s", vcsFile, err))
}
defer vfile.Close()
if !os.IsNotExist(err) {
decoder := json.NewDecoder(vfile)
if err = decoder.Decode(&savedInfo); err != nil {
return errors.New(gotext.Get("failed to read vcs file '%s': %s", vcsFile, err))
}
}
return nil
}
func initHomeDirs() error {
if _, err := os.Stat(configHome); os.IsNotExist(err) {
if err = os.MkdirAll(configHome, 0755); err != nil {
return errors.New(gotext.Get("failed to create config directory '%s': %s", configHome, err))
}
} else if err != nil {
return err
}
if _, err := os.Stat(cacheHome); os.IsNotExist(err) {
if err = os.MkdirAll(cacheHome, 0755); err != nil {
return errors.New(gotext.Get("failed to create cache directory '%s': %s", cacheHome, err))
}
} else if err != nil {
return err
}
return nil
}
func initBuildDir() error {
if _, err := os.Stat(config.BuildDir); os.IsNotExist(err) {
if err = os.MkdirAll(config.BuildDir, 0755); err != nil {
return errors.New(gotext.Get("failed to create BuildDir directory '%s': %s", config.BuildDir, err))
}
} else if err != nil {
return err
}
return nil
}
func initAlpm() error {
var err error
var stderr string
root := "/"
if value, _, exists := cmdArgs.getArg("root", "r"); exists {
root = value
}
pacmanConf, stderr, err = pacmanconf.PacmanConf("--config", config.PacmanConf, "--root", root)
if err != nil {
return fmt.Errorf("%s", stderr)
}
if value, _, exists := cmdArgs.getArg("dbpath", "b"); exists {
pacmanConf.DBPath = value
}
if value, _, exists := cmdArgs.getArg("arch"); exists {
pacmanConf.Architecture = value
}
if value, _, exists := cmdArgs.getArg("ignore"); exists {
pacmanConf.IgnorePkg = append(pacmanConf.IgnorePkg, strings.Split(value, ",")...)
}
if value, _, exists := cmdArgs.getArg("ignoregroup"); exists {
pacmanConf.IgnoreGroup = append(pacmanConf.IgnoreGroup, strings.Split(value, ",")...)
}
// TODO
// current system does not allow duplicate arguments
// but pacman allows multiple cachedirs to be passed
// for now only handle one cache dir
if value, _, exists := cmdArgs.getArg("cachedir"); exists {
pacmanConf.CacheDir = []string{value}
}
if value, _, exists := cmdArgs.getArg("gpgdir"); exists {
pacmanConf.GPGDir = value
}
if err := initAlpmHandle(); err != nil {
return err
}
switch value, _, _ := cmdArgs.getArg("color"); value {
case "always":
text.UseColor = true
case "auto":
text.UseColor = isTty()
case "never":
text.UseColor = false
default:
text.UseColor = pacmanConf.Color && isTty()
}
return nil
}
func initAlpmHandle() error {
if alpmHandle != nil {
if errRelease := alpmHandle.Release(); errRelease != nil {
return errRelease
}
}
var err error
if alpmHandle, err = alpm.Initialize(pacmanConf.RootDir, pacmanConf.DBPath); err != nil {
return errors.New(gotext.Get("unable to CreateHandle: %s", err))
}
if err := configureAlpm(); err != nil {
return err
}
alpmHandle.SetQuestionCallback(questionCallback)
alpmHandle.SetLogCallback(logCallback)
return nil
}
func exitOnError(err error) {
if err != nil {
if str := err.Error(); str != "" {
fmt.Fprintln(os.Stderr, str)
}
cleanup()
os.Exit(1)
}
}
func cleanup() int {
if alpmHandle != nil {
if err := alpmHandle.Release(); err != nil {
fmt.Fprintln(os.Stderr, err)
return 1
}
}
return 0
}
func main() {
initGotext()
if os.Geteuid() == 0 {
text.Warnln(gotext.Get("Avoid running yay as root/sudo."))
fallbackLog := text.NewLogger(os.Stdout, os.Stderr, os.Stdin, false, "fallback")
var (
err error
ctx = context.Background()
ret = 0
)
defer func() {
if rec := recover(); rec != nil {
fallbackLog.Errorln("Panic occurred:", rec)
fallbackLog.Errorln("Stack trace:", string(debug.Stack()))
ret = 1
}
exitOnError(setPaths())
config = defaultSettings()
exitOnError(initHomeDirs())
exitOnError(initConfig())
exitOnError(cmdArgs.parseCommandLine())
if shouldSaveConfig {
err := config.saveConfig()
os.Exit(ret)
}()
initGotext()
if os.Geteuid() == 0 {
fallbackLog.Warnln(gotext.Get("Avoid running yay as root/sudo."))
}
configPath := settings.GetConfigPath()
// Parse config
cfg, err := settings.NewConfig(fallbackLog, configPath, yayVersion)
if err != nil {
fmt.Fprintln(os.Stderr, err)
if str := err.Error(); str != "" {
fallbackLog.Errorln(str)
}
ret = 1
return
}
if errS := cfg.RunMigrations(fallbackLog,
settings.DefaultMigrations(), configPath, yayVersion); errS != nil {
fallbackLog.Errorln(errS)
}
cmdArgs := parser.MakeArguments()
// Parse command line
if err = cfg.ParseCommandLine(cmdArgs); err != nil {
if str := err.Error(); str != "" {
fallbackLog.Errorln(str)
}
ret = 1
return
}
if cfg.SaveConfig {
if errS := cfg.Save(configPath, yayVersion); errS != nil {
fallbackLog.Errorln(errS)
}
}
config.expandEnv()
exitOnError(initBuildDir())
exitOnError(initVCS())
exitOnError(initAlpm())
exitOnError(handleCmd())
os.Exit(cleanup())
// Build run
run, err := runtime.NewRuntime(cfg, cmdArgs, yayVersion)
if err != nil {
if str := err.Error(); str != "" {
fallbackLog.Errorln(str)
}
ret = 1
return
}
dbExecutor, err := ialpm.NewExecutor(run.PacmanConf, run.Logger.Child("db"))
if err != nil {
if str := err.Error(); str != "" {
fallbackLog.Errorln(str)
}
ret = 1
return
}
defer func() {
if rec := recover(); rec != nil {
fallbackLog.Errorln("Panic occurred in DB operation:", rec)
fallbackLog.Errorln("Stack trace:", string(debug.Stack()))
}
dbExecutor.Cleanup()
}()
if err = handleCmd(ctx, run, cmdArgs, dbExecutor); err != nil {
if str := err.Error(); str != "" {
fallbackLog.Errorln(str)
}
exitError := &exec.ExitError{}
if errors.As(err, &exitError) {
// mirror pacman exit code when applicable
ret = exitError.ExitCode()
return
}
// fallback
ret = 1
}
}

914
parser.go
View File

@ -1,914 +0,0 @@
package main
import (
"bufio"
"bytes"
"html"
"os"
"strconv"
"strings"
"github.com/leonelquinteros/gotext"
rpc "github.com/mikkeloscar/aur"
"github.com/pkg/errors"
"github.com/Jguer/yay/v10/pkg/stringset"
)
// Parses command line arguments in a way we can interact with programmatically but
// also in a way that can easily be passed to pacman later on.
type arguments struct {
op string
options map[string]string
globals map[string]string
doubles stringset.StringSet // Tracks args passed twice such as -yy and -dd
targets []string
}
func makeArguments() *arguments {
return &arguments{
"",
make(map[string]string),
make(map[string]string),
make(stringset.StringSet),
make([]string, 0),
}
}
func (parser *arguments) copyGlobal() (cp *arguments) {
cp = makeArguments()
for k, v := range parser.globals {
cp.globals[k] = v
}
return
}
func (parser *arguments) copy() (cp *arguments) {
cp = makeArguments()
cp.op = parser.op
for k, v := range parser.options {
cp.options[k] = v
}
for k, v := range parser.globals {
cp.globals[k] = v
}
cp.targets = make([]string, len(parser.targets))
copy(cp.targets, parser.targets)
for k, v := range parser.doubles {
cp.doubles[k] = v
}
return
}
func (parser *arguments) delArg(options ...string) {
for _, option := range options {
delete(parser.options, option)
delete(parser.globals, option)
delete(parser.doubles, option)
}
}
func (parser *arguments) needRoot() bool {
if parser.existsArg("h", "help") {
return false
}
switch parser.op {
case "D", "database":
if parser.existsArg("k", "check") {
return false
}
return true
case "F", "files":
if parser.existsArg("y", "refresh") {
return true
}
return false
case "Q", "query":
if parser.existsArg("k", "check") {
return true
}
return false
case "R", "remove":
if parser.existsArg("p", "print", "print-format") {
return false
}
return true
case "S", "sync":
if parser.existsArg("y", "refresh") {
return true
}
if parser.existsArg("p", "print", "print-format") {
return false
}
if parser.existsArg("s", "search") {
return false
}
if parser.existsArg("l", "list") {
return false
}
if parser.existsArg("g", "groups") {
return false
}
if parser.existsArg("i", "info") {
return false
}
if parser.existsArg("c", "clean") && mode == modeAUR {
return false
}
return true
case "U", "upgrade":
return true
default:
return false
}
}
func (parser *arguments) addOP(op string) (err error) {
if parser.op != "" {
err = errors.New(gotext.Get("only one operation may be used at a time"))
return
}
parser.op = op
return
}
func (parser *arguments) addParam(option, arg string) (err error) {
if !isArg(option) {
return errors.New(gotext.Get("invalid option '%s'", option))
}
if isOp(option) {
err = parser.addOP(option)
return
}
switch {
case parser.existsArg(option):
parser.doubles[option] = struct{}{}
case isGlobal(option):
parser.globals[option] = arg
default:
parser.options[option] = arg
}
return
}
func (parser *arguments) addArg(options ...string) (err error) {
for _, option := range options {
err = parser.addParam(option, "")
if err != nil {
return
}
}
return
}
// Multiple args acts as an OR operator
func (parser *arguments) existsArg(options ...string) bool {
for _, option := range options {
_, exists := parser.options[option]
if exists {
return true
}
_, exists = parser.globals[option]
if exists {
return true
}
}
return false
}
func (parser *arguments) getArg(options ...string) (arg string, double, exists bool) {
existCount := 0
for _, option := range options {
var value string
value, exists = parser.options[option]
if exists {
arg = value
existCount++
_, exists = parser.doubles[option]
if exists {
existCount++
}
}
value, exists = parser.globals[option]
if exists {
arg = value
existCount++
_, exists = parser.doubles[option]
if exists {
existCount++
}
}
}
double = existCount >= 2
exists = existCount >= 1
return arg, double, exists
}
func (parser *arguments) addTarget(targets ...string) {
parser.targets = append(parser.targets, targets...)
}
func (parser *arguments) clearTargets() {
parser.targets = make([]string, 0)
}
// Multiple args acts as an OR operator
func (parser *arguments) existsDouble(options ...string) bool {
for _, option := range options {
_, exists := parser.doubles[option]
if exists {
return true
}
}
return false
}
func (parser *arguments) formatArgs() (args []string) {
var op string
if parser.op != "" {
op = formatArg(parser.op)
}
args = append(args, op)
for option, arg := range parser.options {
if option == "--" {
continue
}
formattedOption := formatArg(option)
args = append(args, formattedOption)
if hasParam(option) {
args = append(args, arg)
}
if parser.existsDouble(option) {
args = append(args, formattedOption)
}
}
return
}
func (parser *arguments) formatGlobals() (args []string) {
for option, arg := range parser.globals {
formattedOption := formatArg(option)
args = append(args, formattedOption)
if hasParam(option) {
args = append(args, arg)
}
if parser.existsDouble(option) {
args = append(args, formattedOption)
}
}
return
}
func formatArg(arg string) string {
if len(arg) > 1 {
arg = "--" + arg
} else {
arg = "-" + arg
}
return arg
}
func isArg(arg string) bool {
switch arg {
case "-", "--":
case "ask":
case "D", "database":
case "Q", "query":
case "R", "remove":
case "S", "sync":
case "T", "deptest":
case "U", "upgrade":
case "F", "files":
case "V", "version":
case "h", "help":
case "Y", "yay":
case "P", "show":
case "G", "getpkgbuild":
case "b", "dbpath":
case "r", "root":
case "v", "verbose":
case "arch":
case "cachedir":
case "color":
case "config":
case "debug":
case "gpgdir":
case "hookdir":
case "logfile":
case "noconfirm":
case "confirm":
case "disable-download-timeout":
case "sysroot":
case "d", "nodeps":
case "assume-installed":
case "dbonly":
case "absdir":
case "noprogressbar":
case "noscriptlet":
case "p", "print":
case "print-format":
case "asdeps":
case "asexplicit":
case "ignore":
case "ignoregroup":
case "needed":
case "overwrite":
case "f", "force":
case "c", "changelog":
case "deps":
case "e", "explicit":
case "g", "groups":
case "i", "info":
case "k", "check":
case "l", "list":
case "m", "foreign":
case "n", "native":
case "o", "owns":
case "file":
case "q", "quiet":
case "s", "search":
case "t", "unrequired":
case "u", "upgrades":
case "cascade":
case "nosave":
case "recursive":
case "unneeded":
case "clean":
case "sysupgrade":
case "w", "downloadonly":
case "y", "refresh":
case "x", "regex":
case "machinereadable":
// yay options
case "aururl":
case "save":
case "afterclean", "cleanafter":
case "noafterclean", "nocleanafter":
case "devel":
case "nodevel":
case "timeupdate":
case "notimeupdate":
case "topdown":
case "bottomup":
case "completioninterval":
case "sortby":
case "searchby":
case "redownload":
case "redownloadall":
case "noredownload":
case "rebuild":
case "rebuildall":
case "rebuildtree":
case "norebuild":
case "batchinstall":
case "nobatchinstall":
case "answerclean":
case "noanswerclean":
case "answerdiff":
case "noanswerdiff":
case "answeredit":
case "noansweredit":
case "answerupgrade":
case "noanswerupgrade":
case "gpgflags":
case "mflags":
case "gitflags":
case "builddir":
case "editor":
case "editorflags":
case "makepkg":
case "makepkgconf":
case "nomakepkgconf":
case "pacman":
case "git":
case "gpg":
case "sudo":
case "sudoflags":
case "requestsplitn":
case "sudoloop":
case "nosudoloop":
case "provides":
case "noprovides":
case "pgpfetch":
case "nopgpfetch":
case "upgrademenu":
case "noupgrademenu":
case "cleanmenu":
case "nocleanmenu":
case "diffmenu":
case "nodiffmenu":
case "editmenu":
case "noeditmenu":
case "useask":
case "nouseask":
case "combinedupgrade":
case "nocombinedupgrade":
case "a", "aur":
case "repo":
case "removemake":
case "noremovemake":
case "askremovemake":
case "complete":
case "stats":
case "news":
case "gendb":
case "currentconfig":
default:
return false
}
return true
}
func handleConfig(option, value string) bool {
switch option {
case "aururl":
config.AURURL = value
case "save":
shouldSaveConfig = true
case "afterclean", "cleanafter":
config.CleanAfter = true
case "noafterclean", "nocleanafter":
config.CleanAfter = false
case "devel":
config.Devel = true
case "nodevel":
config.Devel = false
case "timeupdate":
config.TimeUpdate = true
case "notimeupdate":
config.TimeUpdate = false
case "topdown":
config.SortMode = topDown
case "bottomup":
config.SortMode = bottomUp
case "completioninterval":
n, err := strconv.Atoi(value)
if err == nil {
config.CompletionInterval = n
}
case "sortby":
config.SortBy = value
case "searchby":
config.SearchBy = value
case "noconfirm":
config.NoConfirm = true
case "config":
config.PacmanConf = value
case "redownload":
config.ReDownload = "yes"
case "redownloadall":
config.ReDownload = "all"
case "noredownload":
config.ReDownload = "no"
case "rebuild":
config.ReBuild = "yes"
case "rebuildall":
config.ReBuild = "all"
case "rebuildtree":
config.ReBuild = "tree"
case "norebuild":
config.ReBuild = "no"
case "batchinstall":
config.BatchInstall = true
case "nobatchinstall":
config.BatchInstall = false
case "answerclean":
config.AnswerClean = value
case "noanswerclean":
config.AnswerClean = ""
case "answerdiff":
config.AnswerDiff = value
case "noanswerdiff":
config.AnswerDiff = ""
case "answeredit":
config.AnswerEdit = value
case "noansweredit":
config.AnswerEdit = ""
case "answerupgrade":
config.AnswerUpgrade = value
case "noanswerupgrade":
config.AnswerUpgrade = ""
case "gpgflags":
config.GpgFlags = value
case "mflags":
config.MFlags = value
case "gitflags":
config.GitFlags = value
case "builddir":
config.BuildDir = value
case "absdir":
config.ABSDir = value
case "editor":
config.Editor = value
case "editorflags":
config.EditorFlags = value
case "makepkg":
config.MakepkgBin = value
case "makepkgconf":
config.MakepkgConf = value
case "nomakepkgconf":
config.MakepkgConf = ""
case "pacman":
config.PacmanBin = value
case "git":
config.GitBin = value
case "gpg":
config.GpgBin = value
case "sudo":
config.SudoBin = value
case "sudoflags":
config.SudoFlags = value
case "requestsplitn":
n, err := strconv.Atoi(value)
if err == nil && n > 0 {
config.RequestSplitN = n
}
case "sudoloop":
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 "useask":
config.UseAsk = true
case "nouseask":
config.UseAsk = false
case "combinedupgrade":
config.CombinedUpgrade = true
case "nocombinedupgrade":
config.CombinedUpgrade = false
case "a", "aur":
mode = modeAUR
case "repo":
mode = modeRepo
case "removemake":
config.RemoveMake = "yes"
case "noremovemake":
config.RemoveMake = "no"
case "askremovemake":
config.RemoveMake = "ask"
default:
return false
}
return true
}
func isOp(op string) bool {
switch op {
case "V", "version":
case "D", "database":
case "F", "files":
case "Q", "query":
case "R", "remove":
case "S", "sync":
case "T", "deptest":
case "U", "upgrade":
// yay specific
case "Y", "yay":
case "P", "show":
case "G", "getpkgbuild":
default:
return false
}
return true
}
func isGlobal(op string) bool {
switch op {
case "b", "dbpath":
case "r", "root":
case "v", "verbose":
case "arch":
case "cachedir":
case "color":
case "config":
case "debug":
case "gpgdir":
case "hookdir":
case "logfile":
case "noconfirm":
case "confirm":
default:
return false
}
return true
}
func hasParam(arg string) bool {
switch arg {
case "dbpath", "b":
case "root", "r":
case "sysroot":
case "config":
case "ignore":
case "assume-installed":
case "overwrite":
case "ask":
case "cachedir":
case "hookdir":
case "logfile":
case "ignoregroup":
case "arch":
case "print-format":
case "gpgdir":
case "color":
// yay params
case "aururl":
case "mflags":
case "gpgflags":
case "gitflags":
case "builddir":
case "absdir":
case "editor":
case "editorflags":
case "makepkg":
case "makepkgconf":
case "pacman":
case "git":
case "gpg":
case "sudo":
case "sudoflags":
case "requestsplitn":
case "answerclean":
case "answerdiff":
case "answeredit":
case "answerupgrade":
case "completioninterval":
case "sortby":
case "searchby":
default:
return false
}
return true
}
// Parses short hand options such as:
// -Syu -b/some/path -
func (parser *arguments) parseShortOption(arg, param string) (usedNext bool, err error) {
if arg == "-" {
err = parser.addArg("-")
return
}
arg = arg[1:]
for k, _char := range arg {
char := string(_char)
if hasParam(char) {
if k < len(arg)-1 {
err = parser.addParam(char, arg[k+1:])
} else {
usedNext = true
err = parser.addParam(char, param)
}
break
} else {
err = parser.addArg(char)
if err != nil {
return
}
}
}
return
}
// Parses full length options such as:
// --sync --refresh --sysupgrade --dbpath /some/path --
func (parser *arguments) parseLongOption(arg, param string) (usedNext bool, err error) {
if arg == "--" {
err = parser.addArg(arg)
return
}
arg = arg[2:]
switch split := strings.SplitN(arg, "=", 2); {
case len(split) == 2:
err = parser.addParam(split[0], split[1])
case hasParam(arg):
err = parser.addParam(arg, param)
usedNext = true
default:
err = parser.addArg(arg)
}
return
}
func (parser *arguments) parseStdin() error {
scanner := bufio.NewScanner(os.Stdin)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
parser.addTarget(scanner.Text())
}
return os.Stdin.Close()
}
func (parser *arguments) parseCommandLine() error {
args := os.Args[1:]
usedNext := false
if len(args) < 1 {
if _, err := parser.parseShortOption("-Syu", ""); err != nil {
return err
}
} else {
for k, arg := range args {
var nextArg string
if usedNext {
usedNext = false
continue
}
if k+1 < len(args) {
nextArg = args[k+1]
}
var err error
switch {
case parser.existsArg("--"):
parser.addTarget(arg)
case strings.HasPrefix(arg, "--"):
usedNext, err = parser.parseLongOption(arg, nextArg)
case strings.HasPrefix(arg, "-"):
usedNext, err = parser.parseShortOption(arg, nextArg)
default:
parser.addTarget(arg)
}
if err != nil {
return err
}
}
}
if parser.op == "" {
parser.op = "Y"
}
if parser.existsArg("-") {
if err := parser.parseStdin(); err != nil {
return err
}
parser.delArg("-")
file, err := os.Open("/dev/tty")
if err != nil {
return err
}
os.Stdin = file
}
cmdArgs.extractYayOptions()
return nil
}
func (parser *arguments) extractYayOptions() {
for option, value := range parser.options {
if handleConfig(option, value) {
parser.delArg(option)
}
}
for option, value := range parser.globals {
if handleConfig(option, value) {
parser.delArg(option)
}
}
rpc.AURURL = strings.TrimRight(config.AURURL, "/") + "/rpc.php?"
config.AURURL = strings.TrimRight(config.AURURL, "/")
}
// 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()
}

82
pkg/cmd/graph/main.go Normal file
View File

@ -0,0 +1,82 @@
package main
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"github.com/Jguer/yay/v12/pkg/db/ialpm"
"github.com/Jguer/yay/v12/pkg/dep"
"github.com/Jguer/yay/v12/pkg/runtime"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
"github.com/Jguer/aur/metadata"
"github.com/leonelquinteros/gotext"
)
func handleCmd(logger *text.Logger) error {
cfg, err := settings.NewConfig(logger, settings.GetConfigPath(), "")
if err != nil {
return err
}
cmdArgs := parser.MakeArguments()
if errP := cfg.ParseCommandLine(cmdArgs); errP != nil {
return errP
}
run, err := runtime.NewRuntime(cfg, cmdArgs, "1.0.0")
if err != nil {
return err
}
dbExecutor, err := ialpm.NewExecutor(run.PacmanConf, logger)
if err != nil {
return err
}
aurCache, err := metadata.New(
metadata.WithCacheFilePath(
filepath.Join(cfg.BuildDir, "aur.json")))
if err != nil {
return fmt.Errorf("%s: %w", gotext.Get("failed to retrieve aur Cache"), err)
}
grapher := dep.NewGrapher(dbExecutor, aurCache, true, settings.NoConfirm,
cmdArgs.ExistsDouble("d", "nodeps"), false, false,
run.Logger.Child("grapher"))
return graphPackage(context.Background(), grapher, cmdArgs.Targets)
}
func main() {
fallbackLog := text.NewLogger(os.Stdout, os.Stderr, os.Stdin, false, "fallback")
if err := handleCmd(fallbackLog); err != nil {
fallbackLog.Errorln(err)
os.Exit(1)
}
}
func graphPackage(
ctx context.Context,
grapher *dep.Grapher,
targets []string,
) error {
if len(targets) != 1 {
return errors.New(gotext.Get("only one target is allowed"))
}
graph, err := grapher.GraphFromAUR(ctx, nil, []string{targets[0]})
if err != nil {
return err
}
fmt.Fprintln(os.Stdout, graph.String())
fmt.Fprintln(os.Stdout, "\nlayers map\n", graph.TopoSortedLayerMap(nil))
return nil
}

View File

@ -2,81 +2,125 @@ package completion
import (
"bufio"
"context"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path"
"path/filepath"
"strings"
"time"
alpm "github.com/Jguer/go-alpm"
"github.com/Jguer/yay/v12/pkg/db"
)
// Show provides completion info for shells
func Show(alpmHandle *alpm.Handle, aurURL, cacheDir string, interval int, force bool) error {
completionPath := filepath.Join(cacheDir, "completion.cache")
type PkgSynchronizer interface {
SyncPackages(...string) []db.IPackage
}
err := Update(alpmHandle, aurURL, cacheDir, interval, force)
type httpRequestDoer interface {
Do(req *http.Request) (*http.Response, error)
}
// Show provides completion info for shells.
func Show(ctx context.Context, httpClient httpRequestDoer,
dbExecutor PkgSynchronizer, aurURL, completionPath string, interval int, force bool,
) error {
err := Update(ctx, httpClient, dbExecutor, aurURL, completionPath, interval, force)
if err != nil {
return err
}
in, err := os.OpenFile(completionPath, os.O_RDWR|os.O_CREATE, 0644)
in, err := os.OpenFile(completionPath, os.O_RDWR|os.O_CREATE, 0o644)
if err != nil {
return err
}
defer in.Close()
_, err = io.Copy(os.Stdout, in)
return err
}
// Update updates completion cache to be used by Complete
func Update(alpmHandle *alpm.Handle, aurURL, cacheDir string, interval int, force bool) error {
completionPath := filepath.Join(cacheDir, "completion.cache")
// Update updates completion cache to be used by Complete.
func Update(ctx context.Context, httpClient httpRequestDoer,
dbExecutor PkgSynchronizer, aurURL, completionPath string, interval int, force bool,
) error {
info, err := os.Stat(completionPath)
if os.IsNotExist(err) || (interval != -1 && time.Since(info.ModTime()).Hours() >= float64(interval*24)) || force {
errd := os.MkdirAll(filepath.Dir(completionPath), 0755)
errd := os.MkdirAll(filepath.Dir(completionPath), 0o755)
if errd != nil {
return errd
}
out, errf := os.Create(completionPath)
if errf != nil {
return errf
}
if createAURList(aurURL, out) != nil {
if createAURList(ctx, httpClient, aurURL, out) != nil {
defer os.Remove(completionPath)
}
erra := createRepoList(alpmHandle, out)
erra := createRepoList(dbExecutor, out)
out.Close()
return erra
}
return nil
}
// CreateAURList creates a new completion file
func createAURList(aurURL string, out io.Writer) error {
// CreateAURList creates a new completion file.
func createAURList(ctx context.Context, client httpRequestDoer, aurURL string, out io.Writer) error {
u, err := url.Parse(aurURL)
if err != nil {
return err
}
u.Path = path.Join(u.Path, "packages.gz")
resp, err := http.Get(u.String())
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), http.NoBody)
if err != nil {
return err
}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("invalid status code: %d", resp.StatusCode)
}
scanner := bufio.NewScanner(resp.Body)
scanner.Scan()
for scanner.Scan() {
_, err = io.WriteString(out, scanner.Text()+"\tAUR\n")
text := scanner.Text()
if strings.HasPrefix(text, "#") {
continue
}
if _, err := io.WriteString(out, text+"\tAUR\n"); err != nil {
return err
}
}
return nil
}
// createRepoList appends Repo packages to completion cache.
func createRepoList(dbExecutor PkgSynchronizer, out io.Writer) error {
for _, pkg := range dbExecutor.SyncPackages() {
_, err := io.WriteString(out, pkg.Name()+"\t"+pkg.DB().Name()+"\n")
if err != nil {
return err
}
@ -84,20 +128,3 @@ func createAURList(aurURL string, out io.Writer) error {
return nil
}
// CreatePackageList appends Repo packages to completion cache
func createRepoList(alpmHandle *alpm.Handle, out io.Writer) error {
dbList, err := alpmHandle.SyncDBs()
if err != nil {
return err
}
_ = dbList.ForEach(func(db alpm.DB) error {
_ = db.PkgCache().ForEach(func(pkg alpm.Package) error {
_, err = io.WriteString(out, pkg.Name()+"\t"+pkg.DB().Name()+"\n")
return err
})
return nil
})
return nil
}

View File

@ -0,0 +1,99 @@
//go:build !integration
// +build !integration
package completion
import (
"bytes"
"context"
"errors"
"io"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
)
const samplePackageResp = `
# AUR package list, generated on Fri, 24 Jul 2020 22:05:22 GMT
cytadela
bitefusion
globs-svn
ri-li
globs-benchmarks-svn
dunelegacy
lumina
eternallands-sound
`
const expectPackageCompletion = `cytadela AUR
bitefusion AUR
globs-svn AUR
ri-li AUR
globs-benchmarks-svn AUR
dunelegacy AUR
lumina AUR
eternallands-sound AUR
`
type mockDoer struct {
t *testing.T
returnBody string
returnStatusCode int
returnErr error
wantUrl string
}
func (m *mockDoer) Do(req *http.Request) (*http.Response, error) {
assert.Equal(m.t, m.wantUrl, req.URL.String())
return &http.Response{
StatusCode: m.returnStatusCode,
Body: io.NopCloser(bytes.NewBufferString(m.returnBody)),
}, m.returnErr
}
func Test_createAURList(t *testing.T) {
t.Parallel()
doer := &mockDoer{
t: t,
wantUrl: "https://aur.archlinux.org/packages.gz",
returnStatusCode: 200,
returnBody: samplePackageResp,
returnErr: nil,
}
out := &bytes.Buffer{}
err := createAURList(context.Background(), doer, "https://aur.archlinux.org", out)
assert.NoError(t, err)
gotOut := out.String()
assert.Equal(t, expectPackageCompletion, gotOut)
}
func Test_createAURListHTTPError(t *testing.T) {
t.Parallel()
doer := &mockDoer{
t: t,
wantUrl: "https://aur.archlinux.org/packages.gz",
returnStatusCode: 200,
returnBody: samplePackageResp,
returnErr: errors.New("Not available"),
}
out := &bytes.Buffer{}
err := createAURList(context.Background(), doer, "https://aur.archlinux.org", out)
assert.EqualError(t, err, "Not available")
}
func Test_createAURListStatusError(t *testing.T) {
t.Parallel()
doer := &mockDoer{
t: t,
wantUrl: "https://aur.archlinux.org/packages.gz",
returnStatusCode: 503,
returnBody: samplePackageResp,
returnErr: nil,
}
out := &bytes.Buffer{}
err := createAURList(context.Background(), doer, "https://aur.archlinux.org", out)
assert.EqualError(t, err, "invalid status code: 503")
}

68
pkg/db/executor.go Normal file
View File

@ -0,0 +1,68 @@
package db
import (
"time"
alpm "github.com/Jguer/go-alpm/v2"
"github.com/Jguer/yay/v12/pkg/text"
)
type (
IPackage = alpm.IPackage
Depend = alpm.Depend
)
// VerCmp performs version comparison according to Pacman conventions. Return
// value is <0 if and only if v1 is older than v2.
func VerCmp(v1, v2 string) int {
return alpm.VerCmp(v1, v2)
}
type Upgrade struct {
Name string
Base string
Repository string
LocalVersion string
RemoteVersion string
Reason alpm.PkgReason
Extra string // Extra information to be displayed
}
type SyncUpgrade struct {
Package alpm.IPackage
LocalVersion string
Reason alpm.PkgReason
}
type Executor interface {
AlpmArchitectures() ([]string, error)
BiggestPackages() []IPackage
Cleanup()
InstalledRemotePackageNames() []string
InstalledRemotePackages() map[string]IPackage
InstalledSyncPackageNames() []string
IsCorrectVersionInstalled(string, string) bool
LastBuildTime() time.Time
LocalPackage(string) IPackage
LocalPackages() []IPackage
LocalSatisfierExists(string) bool
PackageDepends(IPackage) []Depend
PackageGroups(IPackage) []string
PackageOptionalDepends(IPackage) []Depend
PackageProvides(IPackage) []Depend
PackagesFromGroup(string) []IPackage
PackagesFromGroupAndDB(string, string) ([]IPackage, error)
RefreshHandle() error
SyncUpgrades(enableDowngrade bool) (
map[string]SyncUpgrade, error)
Repos() []string
SatisfierFromDB(string, string) (IPackage, error)
SyncPackage(string) IPackage
SyncPackageFromDB(string, string) IPackage
SyncPackages(...string) []IPackage
SyncSatisfier(string) IPackage
SyncSatisfierExists(string) bool
SetLogger(logger *text.Logger)
}

543
pkg/db/ialpm/alpm.go Normal file
View File

@ -0,0 +1,543 @@
package ialpm
import (
"errors"
"fmt"
"os"
"strconv"
"time"
alpm "github.com/Jguer/go-alpm/v2"
pacmanconf "github.com/Morganamilo/go-pacmanconf"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/text"
)
type AlpmExecutor struct {
handle *alpm.Handle
localDB alpm.IDB
syncDB alpm.IDBList
syncDBsCache []alpm.IDB
conf *pacmanconf.Config
log *text.Logger
installedRemotePkgNames []string
installedRemotePkgMap map[string]alpm.IPackage
installedSyncPkgNames []string
}
func NewExecutor(pacmanConf *pacmanconf.Config, logger *text.Logger) (*AlpmExecutor, error) {
ae := &AlpmExecutor{
handle: nil,
localDB: nil,
syncDB: nil,
syncDBsCache: []alpm.IDB{},
conf: pacmanConf,
log: logger,
installedRemotePkgNames: nil,
installedRemotePkgMap: nil,
installedSyncPkgNames: nil,
}
if err := ae.RefreshHandle(); err != nil {
return nil, err
}
var err error
ae.localDB, err = ae.handle.LocalDB()
if err != nil {
return nil, err
}
ae.syncDB, err = ae.handle.SyncDBs()
if err != nil {
return nil, err
}
return ae, nil
}
func toUsage(usages []string) alpm.Usage {
if len(usages) == 0 {
return alpm.UsageAll
}
var ret alpm.Usage
for _, usage := range usages {
switch usage {
case "Sync":
ret |= alpm.UsageSync
case "Search":
ret |= alpm.UsageSearch
case "Install":
ret |= alpm.UsageInstall
case "Upgrade":
ret |= alpm.UsageUpgrade
case "All":
ret |= alpm.UsageAll
}
}
return ret
}
func configureAlpm(pacmanConf *pacmanconf.Config, alpmHandle *alpm.Handle) error {
for _, repo := range pacmanConf.Repos {
// TODO: set SigLevel
alpmDB, err := alpmHandle.RegisterSyncDB(repo.Name, 0)
if err != nil {
return err
}
alpmDB.SetServers(repo.Servers)
alpmDB.SetUsage(toUsage(repo.Usage))
}
if err := alpmHandle.SetCacheDirs(pacmanConf.CacheDir); err != nil {
return err
}
// add hook directories 1-by-1 to avoid overwriting the system directory
for _, dir := range pacmanConf.HookDir {
if err := alpmHandle.AddHookDir(dir); err != nil {
return err
}
}
if err := alpmHandle.SetGPGDir(pacmanConf.GPGDir); err != nil {
return err
}
if err := alpmHandle.SetLogFile(pacmanConf.LogFile); err != nil {
return err
}
if err := alpmHandle.SetIgnorePkgs(pacmanConf.IgnorePkg); err != nil {
return err
}
if err := alpmHandle.SetIgnoreGroups(pacmanConf.IgnoreGroup); err != nil {
return err
}
if err := alpmSetArchitecture(alpmHandle, pacmanConf.Architecture); err != nil {
return err
}
if err := alpmHandle.SetNoUpgrades(pacmanConf.NoUpgrade); err != nil {
return err
}
if err := alpmHandle.SetNoExtracts(pacmanConf.NoExtract); err != nil {
return err
}
if err := alpmHandle.SetUseSyslog(pacmanConf.UseSyslog); err != nil {
return err
}
return alpmHandle.SetCheckSpace(pacmanConf.CheckSpace)
}
func (ae *AlpmExecutor) logCallback() func(level alpm.LogLevel, str string) {
return func(level alpm.LogLevel, str string) {
switch level {
case alpm.LogWarning:
ae.log.Warn(str)
case alpm.LogError:
ae.log.Error(str)
}
}
}
func (ae *AlpmExecutor) questionCallback() func(question alpm.QuestionAny) {
return func(question alpm.QuestionAny) {
if qi, err := question.QuestionInstallIgnorepkg(); err == nil {
qi.SetInstall(true)
}
qp, err := question.QuestionSelectProvider()
if err != nil {
return
}
if settings.HideMenus {
return
}
size := 0
_ = qp.Providers(ae.handle).ForEach(func(pkg alpm.IPackage) error {
size++
return nil
})
str := text.Bold(gotext.Get("There are %[1]d providers available for %[2]s:", size, qp.Dep()))
size = 1
var dbName string
_ = qp.Providers(ae.handle).ForEach(func(pkg alpm.IPackage) error {
thisDB := pkg.DB().Name()
if dbName != thisDB {
dbName = thisDB
str += "\n"
str += ae.log.SprintOperationInfo(gotext.Get("Repository"), " ", dbName, "\n ")
}
str += fmt.Sprintf("%d) %s ", size, pkg.Name())
size++
return nil
})
ae.log.OperationInfoln(str)
for {
ae.log.Println(gotext.Get("\nEnter a number (default=1): "))
// TODO: reenable noconfirm
if settings.NoConfirm {
ae.log.Println()
break
}
numberBuf, err := ae.log.GetInput("", false)
if err != nil {
ae.log.Errorln(err)
break
}
if numberBuf == "" {
break
}
num, err := strconv.Atoi(numberBuf)
if err != nil {
ae.log.Errorln(gotext.Get("invalid number: %s", numberBuf))
continue
}
if num < 1 || num > size {
ae.log.Errorln(gotext.Get("invalid value: %d is not between %d and %d", num, 1, size))
continue
}
qp.SetUseIndex(num - 1)
break
}
}
}
func (ae *AlpmExecutor) RefreshHandle() error {
if ae.handle != nil {
if errRelease := ae.handle.Release(); errRelease != nil {
return errRelease
}
}
alpmHandle, err := alpm.Initialize(ae.conf.RootDir, ae.conf.DBPath)
if err != nil {
return errors.New(gotext.Get("unable to CreateHandle: %s", err))
}
if errConf := configureAlpm(ae.conf, alpmHandle); errConf != nil {
return errConf
}
alpmSetQuestionCallback(alpmHandle, ae.questionCallback())
alpmSetLogCallback(alpmHandle, ae.logCallback())
ae.handle = alpmHandle
ae.syncDBsCache = nil
ae.syncDB, err = alpmHandle.SyncDBs()
if err != nil {
return err
}
ae.localDB, err = alpmHandle.LocalDB()
return err
}
func (ae *AlpmExecutor) LocalSatisfierExists(pkgName string) bool {
if _, err := ae.localDB.PkgCache().FindSatisfier(pkgName); err != nil {
return false
}
return true
}
func (ae *AlpmExecutor) SyncSatisfierExists(pkgName string) bool {
if _, err := ae.syncDB.FindSatisfier(pkgName); err != nil {
return false
}
return true
}
func (ae *AlpmExecutor) IsCorrectVersionInstalled(pkgName, versionRequired string) bool {
alpmPackage := ae.localDB.Pkg(pkgName)
if alpmPackage == nil {
return false
}
return alpmPackage.Version() == versionRequired
}
func (ae *AlpmExecutor) SyncSatisfier(pkgName string) alpm.IPackage {
foundPkg, err := ae.syncDB.FindSatisfier(pkgName)
if err != nil {
return nil
}
return foundPkg
}
func (ae *AlpmExecutor) PackagesFromGroup(groupName string) []alpm.IPackage {
groupPackages := []alpm.IPackage{}
_ = ae.syncDB.FindGroupPkgs(groupName).ForEach(func(pkg alpm.IPackage) error {
groupPackages = append(groupPackages, pkg)
return nil
})
return groupPackages
}
func (ae *AlpmExecutor) PackagesFromGroupAndDB(groupName, dbName string) ([]alpm.IPackage, error) {
singleDBList, err := ae.handle.SyncDBListByDBName(dbName)
if err != nil {
return nil, err
}
groupPackages := []alpm.IPackage{}
_ = singleDBList.FindGroupPkgs(groupName).ForEach(func(pkg alpm.IPackage) error {
groupPackages = append(groupPackages, pkg)
return nil
})
return groupPackages, nil
}
func (ae *AlpmExecutor) LocalPackages() []alpm.IPackage {
localPackages := []alpm.IPackage{}
_ = ae.localDB.PkgCache().ForEach(func(pkg alpm.IPackage) error {
localPackages = append(localPackages, pkg)
return nil
})
return localPackages
}
// SyncPackages searches SyncDB for packages or returns all packages if no search param is given.
func (ae *AlpmExecutor) SyncPackages(pkgNames ...string) []alpm.IPackage {
repoPackages := []alpm.IPackage{}
_ = ae.syncDB.ForEach(func(alpmDB alpm.IDB) error {
if len(pkgNames) == 0 {
_ = alpmDB.PkgCache().ForEach(func(pkg alpm.IPackage) error {
repoPackages = append(repoPackages, pkg)
return nil
})
} else {
_ = alpmDB.Search(pkgNames).ForEach(func(pkg alpm.IPackage) error {
repoPackages = append(repoPackages, pkg)
return nil
})
}
return nil
})
return repoPackages
}
func (ae *AlpmExecutor) LocalPackage(pkgName string) alpm.IPackage {
pkg := ae.localDB.Pkg(pkgName)
if pkg == nil {
return nil
}
return pkg
}
func (ae *AlpmExecutor) syncDBs() []alpm.IDB {
if ae.syncDBsCache == nil {
ae.syncDBsCache = ae.syncDB.Slice()
}
return ae.syncDBsCache
}
func (ae *AlpmExecutor) SyncPackage(pkgName string) alpm.IPackage {
for _, db := range ae.syncDBs() {
if dbPkg := db.Pkg(pkgName); dbPkg != nil {
return dbPkg
}
}
return nil
}
func (ae *AlpmExecutor) SyncPackageFromDB(pkgName, dbName string) alpm.IPackage {
singleDB, err := ae.handle.SyncDBByName(dbName)
if err != nil {
return nil
}
return singleDB.Pkg(pkgName)
}
func (ae *AlpmExecutor) SatisfierFromDB(pkgName, dbName string) (alpm.IPackage, error) {
singleDBList, err := ae.handle.SyncDBListByDBName(dbName)
if err != nil {
return nil, err
}
foundPkg, err := singleDBList.FindSatisfier(pkgName)
if err != nil {
return nil, nil
}
return foundPkg, nil
}
func (ae *AlpmExecutor) PackageDepends(pkg alpm.IPackage) []alpm.Depend {
alpmPackage := pkg.(*alpm.Package)
return alpmPackage.Depends().Slice()
}
func (ae *AlpmExecutor) PackageOptionalDepends(pkg alpm.IPackage) []alpm.Depend {
alpmPackage := pkg.(*alpm.Package)
return alpmPackage.OptionalDepends().Slice()
}
func (ae *AlpmExecutor) PackageProvides(pkg alpm.IPackage) []alpm.Depend {
alpmPackage := pkg.(*alpm.Package)
return alpmPackage.Provides().Slice()
}
func (ae *AlpmExecutor) PackageGroups(pkg alpm.IPackage) []string {
alpmPackage := pkg.(*alpm.Package)
return alpmPackage.Groups().Slice()
}
// upRepo gathers local packages and checks if they have new versions.
// Output: Upgrade type package list.
func (ae *AlpmExecutor) SyncUpgrades(enableDowngrade bool) (
map[string]db.SyncUpgrade, error,
) {
ups := map[string]db.SyncUpgrade{}
var errReturn error
localDB, errDB := ae.handle.LocalDB()
if errDB != nil {
return ups, errDB
}
if err := ae.handle.TransInit(alpm.TransFlagNoLock); err != nil {
return ups, err
}
defer func() {
errReturn = ae.handle.TransRelease()
}()
if err := ae.handle.SyncSysupgrade(enableDowngrade); err != nil {
return ups, err
}
_ = ae.handle.TransGetAdd().ForEach(func(pkg alpm.IPackage) error {
localVer := "-"
reason := alpm.PkgReasonExplicit
if localPkg := localDB.Pkg(pkg.Name()); localPkg != nil {
localVer = localPkg.Version()
reason = localPkg.Reason()
}
ups[pkg.Name()] = db.SyncUpgrade{
Package: pkg,
Reason: reason,
LocalVersion: localVer,
}
return nil
})
return ups, errReturn
}
func (ae *AlpmExecutor) BiggestPackages() []alpm.IPackage {
localPackages := []alpm.IPackage{}
_ = ae.localDB.PkgCache().SortBySize().ForEach(func(pkg alpm.IPackage) error {
localPackages = append(localPackages, pkg)
return nil
})
return localPackages
}
func (ae *AlpmExecutor) LastBuildTime() time.Time {
var lastTime time.Time
_ = ae.syncDB.ForEach(func(db alpm.IDB) error {
_ = db.PkgCache().ForEach(func(pkg alpm.IPackage) error {
thisTime := pkg.BuildDate()
if thisTime.After(lastTime) {
lastTime = thisTime
}
return nil
})
return nil
})
return lastTime
}
func (ae *AlpmExecutor) Cleanup() {
if ae.handle != nil {
if err := ae.handle.Release(); err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
}
func (ae *AlpmExecutor) Repos() (repos []string) {
_ = ae.syncDB.ForEach(func(db alpm.IDB) error {
repos = append(repos, db.Name())
return nil
})
return
}
func alpmSetArchitecture(alpmHandle *alpm.Handle, arch []string) error {
return alpmHandle.SetArchitectures(arch)
}
func (ae *AlpmExecutor) AlpmArchitectures() ([]string, error) {
architectures, err := ae.handle.GetArchitectures()
return architectures.Slice(), err
}
func alpmSetLogCallback(alpmHandle *alpm.Handle, cb func(alpm.LogLevel, string)) {
alpmHandle.SetLogCallback(func(ctx interface{}, lvl alpm.LogLevel, msg string) {
cbo := ctx.(func(alpm.LogLevel, string))
cbo(lvl, msg)
}, cb)
}
func alpmSetQuestionCallback(alpmHandle *alpm.Handle, cb func(alpm.QuestionAny)) {
alpmHandle.SetQuestionCallback(func(ctx interface{}, q alpm.QuestionAny) {
cbo := ctx.(func(alpm.QuestionAny))
cbo(q)
}, cb)
}

116
pkg/db/ialpm/alpm_test.go Normal file
View File

@ -0,0 +1,116 @@
//go:build !integration
// +build !integration
package ialpm
import (
"io"
"strings"
"testing"
alpm "github.com/Jguer/go-alpm/v2"
"github.com/Morganamilo/go-pacmanconf"
"github.com/stretchr/testify/assert"
"github.com/Jguer/yay/v12/pkg/text"
)
func TestAlpmExecutor(t *testing.T) {
t.Parallel()
pacmanConf := &pacmanconf.Config{
RootDir: "/",
DBPath: "/var/lib/pacman/",
CacheDir: []string{"/cachedir/", "/another/"},
HookDir: []string{"/hookdir/"},
GPGDir: "/gpgdir/",
LogFile: "/logfile",
HoldPkg: []string(nil),
IgnorePkg: []string{"ignore", "this", "package"},
IgnoreGroup: []string{"ignore", "this", "group"},
Architecture: []string{"8086"},
XferCommand: "",
NoUpgrade: []string{"noupgrade"},
NoExtract: []string{"noextract"},
CleanMethod: []string{"KeepInstalled"},
SigLevel: []string{"PackageOptional", "PackageTrustedOnly", "DatabaseOptional", "DatabaseTrustedOnly"},
LocalFileSigLevel: []string(nil),
RemoteFileSigLevel: []string(nil),
UseSyslog: false,
Color: false,
UseDelta: 0,
TotalDownload: true,
CheckSpace: true,
VerbosePkgLists: true,
DisableDownloadTimeout: false,
Repos: []pacmanconf.Repository{
{Name: "repo1", Servers: []string{"repo1"}, SigLevel: []string(nil), Usage: []string{"All"}},
{Name: "repo2", Servers: []string{"repo2"}, SigLevel: []string(nil), Usage: []string{"All"}},
},
}
aExec, err := NewExecutor(pacmanConf, text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), false, "test"))
assert.NoError(t, err)
assert.NotNil(t, aExec.conf)
assert.EqualValues(t, pacmanConf, aExec.conf)
assert.NotNil(t, aExec.localDB)
assert.NotNil(t, aExec.syncDB)
assert.NotNil(t, aExec.questionCallback)
h := aExec.handle
assert.NotNil(t, h)
root, err := h.Root()
assert.Nil(t, err)
assert.Equal(t, "/", root)
dbPath, err := h.DBPath()
assert.Nil(t, err)
assert.Equal(t, "/var/lib/pacman/", dbPath)
cache, err := h.CacheDirs()
assert.Nil(t, err)
assert.Equal(t, []string{"/cachedir/", "/another/"}, cache.Slice())
log, err := h.LogFile()
assert.Nil(t, err)
assert.Equal(t, "/logfile", log)
gpg, err := h.GPGDir()
assert.Nil(t, err)
assert.Equal(t, "/gpgdir/", gpg)
hook, err := h.HookDirs()
assert.Nil(t, err)
assert.Equal(t, []string{"/usr/share/libalpm/hooks/", "/hookdir/"}, hook.Slice())
arch, err := alpmTestGetArch(h)
assert.Nil(t, err)
assert.Equal(t, []string{"8086"}, arch)
ignorePkg, err := h.IgnorePkgs()
assert.Nil(t, err)
assert.Equal(t, []string{"ignore", "this", "package"}, ignorePkg.Slice())
ignoreGroup, err := h.IgnoreGroups()
assert.Nil(t, err)
assert.Equal(t, []string{"ignore", "this", "group"}, ignoreGroup.Slice())
noUp, err := h.NoUpgrades()
assert.Nil(t, err)
assert.Equal(t, []string{"noupgrade"}, noUp.Slice())
noEx, err := h.NoExtracts()
assert.Nil(t, err)
assert.Equal(t, []string{"noextract"}, noEx.Slice())
check, err := h.CheckSpace()
assert.Nil(t, err)
assert.Equal(t, true, check)
}
func alpmTestGetArch(h *alpm.Handle) ([]string, error) {
architectures, err := h.GetArchitectures()
return architectures.Slice(), err
}

View File

@ -0,0 +1,54 @@
package ialpm
import (
alpm "github.com/Jguer/go-alpm/v2"
"github.com/Jguer/yay/v12/pkg/text"
)
// GetPackageNamesBySource returns package names with and without correspondence in SyncDBS respectively.
func (ae *AlpmExecutor) getPackageNamesBySource() {
if ae.installedRemotePkgMap == nil {
ae.installedRemotePkgMap = map[string]alpm.IPackage{}
}
for _, localpkg := range ae.LocalPackages() {
pkgName := localpkg.Name()
if ae.SyncPackage(pkgName) != nil {
ae.installedSyncPkgNames = append(ae.installedSyncPkgNames, pkgName)
} else {
ae.installedRemotePkgNames = append(ae.installedRemotePkgNames, pkgName)
ae.installedRemotePkgMap[pkgName] = localpkg
}
}
ae.log.Debugln("populating db executor package caches.",
"sync_len", len(ae.installedSyncPkgNames), "remote_len", len(ae.installedRemotePkgNames))
}
func (ae *AlpmExecutor) InstalledRemotePackages() map[string]alpm.IPackage {
if ae.installedRemotePkgMap == nil {
ae.getPackageNamesBySource()
}
return ae.installedRemotePkgMap
}
func (ae *AlpmExecutor) InstalledRemotePackageNames() []string {
if ae.installedRemotePkgNames == nil {
ae.getPackageNamesBySource()
}
return ae.installedRemotePkgNames
}
func (ae *AlpmExecutor) InstalledSyncPackageNames() []string {
if ae.installedSyncPkgNames == nil {
ae.getPackageNamesBySource()
}
return ae.installedSyncPkgNames
}
func (ae *AlpmExecutor) SetLogger(logger *text.Logger) {
ae.log = logger
}

214
pkg/db/mock/executor.go Normal file
View File

@ -0,0 +1,214 @@
package mock
import (
"time"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/text"
"github.com/Jguer/go-alpm/v2"
)
type (
IPackage = alpm.IPackage
Depend = alpm.Depend
Upgrade = db.Upgrade
)
type DBExecutor struct {
db.Executor
AlpmArchitecturesFn func() ([]string, error)
InstalledRemotePackageNamesFn func() []string
InstalledRemotePackagesFn func() map[string]IPackage
IsCorrectVersionInstalledFn func(string, string) bool
LocalPackageFn func(string) IPackage
LocalPackagesFn func() []IPackage
LocalSatisfierExistsFn func(string) bool
PackageDependsFn func(IPackage) []Depend
PackageOptionalDependsFn func(alpm.IPackage) []alpm.Depend
PackageProvidesFn func(IPackage) []Depend
PackagesFromGroupFn func(string) []IPackage
PackagesFromGroupAndDBFn func(string, string) ([]IPackage, error)
RefreshHandleFn func() error
ReposFn func() []string
SyncPackageFn func(string) IPackage
SyncPackagesFn func(...string) []IPackage
SyncSatisfierFn func(string) IPackage
SatisfierFromDBFn func(string, string) (IPackage, error)
SyncUpgradesFn func(bool) (map[string]db.SyncUpgrade, error)
SetLoggerFn func(*text.Logger)
}
func (t *DBExecutor) InstalledRemotePackageNames() []string {
if t.InstalledRemotePackageNamesFn != nil {
return t.InstalledRemotePackageNamesFn()
}
panic("implement me")
}
func (t *DBExecutor) InstalledRemotePackages() map[string]IPackage {
if t.InstalledRemotePackagesFn != nil {
return t.InstalledRemotePackagesFn()
}
panic("implement me")
}
func (t *DBExecutor) AlpmArchitectures() ([]string, error) {
if t.AlpmArchitecturesFn != nil {
return t.AlpmArchitecturesFn()
}
panic("implement me")
}
func (t *DBExecutor) BiggestPackages() []IPackage {
panic("implement me")
}
func (t *DBExecutor) Cleanup() {
panic("implement me")
}
func (t *DBExecutor) IsCorrectVersionInstalled(s, s2 string) bool {
if t.IsCorrectVersionInstalledFn != nil {
return t.IsCorrectVersionInstalledFn(s, s2)
}
panic("implement me")
}
func (t *DBExecutor) LastBuildTime() time.Time {
panic("implement me")
}
func (t *DBExecutor) LocalPackage(s string) IPackage {
if t.LocalPackageFn != nil {
return t.LocalPackageFn(s)
}
panic("implement me")
}
func (t *DBExecutor) LocalPackages() []IPackage {
if t.LocalPackagesFn != nil {
return t.LocalPackagesFn()
}
panic("implement me")
}
func (t *DBExecutor) LocalSatisfierExists(s string) bool {
if t.LocalSatisfierExistsFn != nil {
return t.LocalSatisfierExistsFn(s)
}
panic("implement me")
}
func (t *DBExecutor) PackageConflicts(iPackage IPackage) []Depend {
panic("implement me")
}
func (t *DBExecutor) PackageDepends(iPackage IPackage) []Depend {
if t.PackageDependsFn != nil {
return t.PackageDependsFn(iPackage)
}
panic("implement me")
}
func (t *DBExecutor) PackageGroups(iPackage IPackage) []string {
return []string{}
}
func (t *DBExecutor) PackageOptionalDepends(iPackage IPackage) []Depend {
if t.PackageOptionalDependsFn != nil {
return t.PackageOptionalDependsFn(iPackage)
}
panic("implement me")
}
func (t *DBExecutor) PackageProvides(iPackage IPackage) []Depend {
if t.PackageProvidesFn != nil {
return t.PackageProvidesFn(iPackage)
}
panic("implement me")
}
func (t *DBExecutor) PackagesFromGroup(s string) []IPackage {
if t.PackagesFromGroupFn != nil {
return t.PackagesFromGroupFn(s)
}
panic("implement me")
}
func (t *DBExecutor) PackagesFromGroupAndDB(s, s2 string) ([]IPackage, error) {
if t.PackagesFromGroupAndDBFn != nil {
return t.PackagesFromGroupAndDBFn(s, s2)
}
panic("implement me")
}
func (t *DBExecutor) RefreshHandle() error {
if t.RefreshHandleFn != nil {
return t.RefreshHandleFn()
}
panic("implement me")
}
func (t *DBExecutor) SyncUpgrades(b bool) (map[string]db.SyncUpgrade, error) {
if t.SyncUpgradesFn != nil {
return t.SyncUpgradesFn(b)
}
panic("implement me")
}
func (t *DBExecutor) Repos() []string {
if t.ReposFn != nil {
return t.ReposFn()
}
panic("implement me")
}
func (t *DBExecutor) SatisfierFromDB(s, s2 string) (IPackage, error) {
if t.SatisfierFromDBFn != nil {
return t.SatisfierFromDBFn(s, s2)
}
panic("implement me")
}
func (t *DBExecutor) SyncPackage(s string) IPackage {
if t.SyncPackageFn != nil {
return t.SyncPackageFn(s)
}
panic("implement me")
}
func (t *DBExecutor) SyncPackages(s ...string) []IPackage {
if t.SyncPackagesFn != nil {
return t.SyncPackagesFn(s...)
}
panic("implement me")
}
func (t *DBExecutor) SyncSatisfier(s string) IPackage {
if t.SyncSatisfierFn != nil {
return t.SyncSatisfierFn(s)
}
panic("implement me")
}
func (t *DBExecutor) SyncSatisfierExists(s string) bool {
if t.SyncSatisfierFn != nil {
return t.SyncSatisfierFn(s) != nil
}
panic("implement me")
}
func (t *DBExecutor) SetLogger(logger *text.Logger) {
if t.SetLoggerFn != nil {
t.SetLoggerFn(logger)
return
}
panic("implement me")
}

229
pkg/db/mock/repo.go Normal file
View File

@ -0,0 +1,229 @@
package mock
import (
"time"
alpm "github.com/Jguer/go-alpm/v2"
)
type DependList struct {
Depends []Depend
}
func (d DependList) Slice() []alpm.Depend {
return d.Depends
}
func (d DependList) ForEach(f func(*alpm.Depend) error) error {
for i := range d.Depends {
dep := &d.Depends[i]
err := f(dep)
if err != nil {
return err
}
}
return nil
}
type Package struct {
PBase string
PBuildDate time.Time
PDB *DB
PDescription string
PISize int64
PName string
PShouldIgnore bool
PSize int64
PVersion string
PReason alpm.PkgReason
PDepends alpm.IDependList
PProvides alpm.IDependList
}
func (p *Package) Base() string {
return p.PBase
}
func (p *Package) BuildDate() time.Time {
return p.PBuildDate
}
func (p *Package) DB() alpm.IDB {
return p.PDB
}
func (p *Package) Description() string {
return p.PDescription
}
func (p *Package) ISize() int64 {
return p.PISize
}
func (p *Package) Name() string {
return p.PName
}
func (p *Package) ShouldIgnore() bool {
return p.PShouldIgnore
}
func (p *Package) Size() int64 {
return p.PSize
}
func (p *Package) Version() string {
return p.PVersion
}
func (p *Package) Reason() alpm.PkgReason {
return p.PReason
}
func (p *Package) FileName() string {
panic("not implemented")
}
func (p *Package) Base64Signature() string {
panic("not implemented")
}
func (p *Package) Validation() alpm.Validation {
panic("not implemented")
}
// Architecture returns the package target Architecture.
func (p *Package) Architecture() string {
panic("not implemented")
}
// Backup returns a list of package backups.
func (p *Package) Backup() alpm.BackupList {
panic("not implemented")
}
// Conflicts returns the conflicts of the package as a DependList.
func (p *Package) Conflicts() alpm.IDependList {
panic("not implemented")
}
// Depends returns the package's dependency list.
func (p *Package) Depends() alpm.IDependList {
if p.PDepends != nil {
return p.PDepends
}
return alpm.DependList{}
}
// Depends returns the package's optional dependency list.
func (p *Package) OptionalDepends() alpm.IDependList {
panic("not implemented")
}
// Depends returns the package's check dependency list.
func (p *Package) CheckDepends() alpm.IDependList {
panic("not implemented")
}
// Depends returns the package's make dependency list.
func (p *Package) MakeDepends() alpm.IDependList {
panic("not implemented")
}
// Files returns the file list of the package.
func (p *Package) Files() []alpm.File {
panic("not implemented")
}
// ContainsFile checks if the path is in the package filelist.
func (p *Package) ContainsFile(path string) (alpm.File, error) {
panic("not implemented")
}
// Groups returns the groups the package belongs to.
func (p *Package) Groups() alpm.StringList {
panic("not implemented")
}
// InstallDate returns the package install date.
func (p *Package) InstallDate() time.Time {
panic("not implemented")
}
// Licenses returns the package license list.
func (p *Package) Licenses() alpm.StringList {
panic("not implemented")
}
// SHA256Sum returns package SHA256Sum.
func (p *Package) SHA256Sum() string {
panic("not implemented")
}
// MD5Sum returns package MD5Sum.
func (p *Package) MD5Sum() string {
panic("not implemented")
}
// Packager returns package packager name.
func (p *Package) Packager() string {
panic("not implemented")
}
// Provides returns DependList of packages provides by package.
func (p *Package) Provides() alpm.IDependList {
if p.PProvides == nil {
return alpm.DependList{}
}
return p.PProvides
}
// Origin returns package origin.
func (p *Package) Origin() alpm.PkgFrom {
panic("not implemented")
}
// Replaces returns a DependList with the packages this package replaces.
func (p *Package) Replaces() alpm.IDependList {
panic("not implemented")
}
// URL returns the upstream URL of the package.
func (p *Package) URL() string {
panic("not implemented")
}
// ComputeRequiredBy returns the names of reverse dependencies of a package.
func (p *Package) ComputeRequiredBy() []string {
panic("not implemented")
}
// ComputeOptionalFor returns the names of packages that optionally
// require the given package.
func (p *Package) ComputeOptionalFor() []string {
panic("not implemented")
}
// SyncNewVersion checks if there is a new version of the
// package in a given DBlist.
func (p *Package) SyncNewVersion(l alpm.IDBList) alpm.IPackage {
panic("not implemented")
}
func (p *Package) Type() string {
panic("not implemented")
}
type DB struct {
alpm.IDB
name string
}
func NewDB(name string) *DB {
return &DB{name: name}
}
func (d *DB) Name() string {
return d.name
}

15
pkg/db/types.go Normal file
View File

@ -0,0 +1,15 @@
package db
func ArchIsSupported(alpmArch []string, arch string) bool {
if arch == "any" {
return true
}
for _, a := range alpmArch {
if a == arch {
return true
}
}
return false
}

87
pkg/dep/dep.go Normal file
View File

@ -0,0 +1,87 @@
package dep
import (
"strings"
"github.com/Jguer/yay/v12/pkg/db"
aur "github.com/Jguer/yay/v12/pkg/query"
)
func splitDep(dep string) (pkg, mod, ver string) {
split := strings.FieldsFunc(dep, func(c rune) bool {
match := c == '>' || c == '<' || c == '='
if match {
mod += string(c)
}
return match
})
if len(split) == 0 {
return "", "", ""
}
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, pkgVersion string) bool {
depName, depMod, depVersion := splitDep(dep)
provideName, provideMod, provideVersion := splitDep(provide)
if provideName != depName {
return false
}
// Unversioned provides can not satisfy a versioned dep
if provideMod == "" && depMod != "" {
provideVersion = pkgVersion // Example package: pagure
}
return verSatisfies(provideVersion, depMod, depVersion)
}
func verSatisfies(ver1, mod, ver2 string) bool {
switch mod {
case "=":
return db.VerCmp(ver1, ver2) == 0
case "<":
return db.VerCmp(ver1, ver2) < 0
case "<=":
return db.VerCmp(ver1, ver2) <= 0
case ">":
return db.VerCmp(ver1, ver2) > 0
case ">=":
return db.VerCmp(ver1, ver2) >= 0
}
return true
}
func satisfiesAur(dep string, pkg *aur.Pkg) bool {
if pkgSatisfies(pkg.Name, pkg.Version, dep) {
return true
}
for _, provide := range pkg.Provides {
if provideSatisfies(provide, dep, pkg.Version) {
return true
}
}
return false
}

853
pkg/dep/dep_graph.go Normal file
View File

@ -0,0 +1,853 @@
package dep
import (
"context"
"fmt"
"strconv"
aurc "github.com/Jguer/aur"
alpm "github.com/Jguer/go-alpm/v2"
gosrc "github.com/Morganamilo/go-srcinfo"
mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/dep/topo"
"github.com/Jguer/yay/v12/pkg/intrange"
aur "github.com/Jguer/yay/v12/pkg/query"
"github.com/Jguer/yay/v12/pkg/text"
)
type InstallInfo struct {
Source Source
Reason Reason
Version string
LocalVersion string
SrcinfoPath *string
AURBase *string
SyncDBName *string
IsGroup bool
Upgrade bool
Devel bool
}
func (i *InstallInfo) String() string {
return fmt.Sprintf("InstallInfo{Source: %v, Reason: %v}", i.Source, i.Reason)
}
type (
Reason uint
Source int
)
func (r Reason) String() string {
return ReasonNames[r]
}
func (s Source) String() string {
return SourceNames[s]
}
const (
Explicit Reason = iota // 0
Dep // 1
MakeDep // 2
CheckDep // 3
)
var ReasonNames = map[Reason]string{
Explicit: gotext.Get("Explicit"),
Dep: gotext.Get("Dependency"),
MakeDep: gotext.Get("Make Dependency"),
CheckDep: gotext.Get("Check Dependency"),
}
const (
AUR Source = iota
Sync
Local
SrcInfo
Missing
)
var SourceNames = map[Source]string{
AUR: gotext.Get("AUR"),
Sync: gotext.Get("Sync"),
Local: gotext.Get("Local"),
SrcInfo: gotext.Get("SRCINFO"),
Missing: gotext.Get("Missing"),
}
var bgColorMap = map[Source]string{
AUR: "lightblue",
Sync: "lemonchiffon",
Local: "darkolivegreen1",
Missing: "tomato",
}
var colorMap = map[Reason]string{
Explicit: "black",
Dep: "deeppink",
MakeDep: "navyblue",
CheckDep: "forestgreen",
}
type Grapher struct {
logger *text.Logger
providerCache map[string][]aur.Pkg
dbExecutor db.Executor
aurClient aurc.QueryClient
fullGraph bool // If true, the graph will include all dependencies including already installed ones or repo
noConfirm bool // If true, the graph will not prompt for confirmation
noDeps bool // If true, the graph will not include dependencies
noCheckDeps bool // If true, the graph will not include check dependencies
needed bool // If true, the graph will only include packages that are not installed
}
func NewGrapher(dbExecutor db.Executor, aurCache aurc.QueryClient,
fullGraph, noConfirm, noDeps, noCheckDeps, needed bool,
logger *text.Logger,
) *Grapher {
return &Grapher{
dbExecutor: dbExecutor,
aurClient: aurCache,
fullGraph: fullGraph,
noConfirm: noConfirm,
noDeps: noDeps,
noCheckDeps: noCheckDeps,
needed: needed,
providerCache: make(map[string][]aurc.Pkg, 5),
logger: logger,
}
}
func NewGraph() *topo.Graph[string, *InstallInfo] {
return topo.New[string, *InstallInfo]()
}
func (g *Grapher) GraphFromTargets(ctx context.Context,
graph *topo.Graph[string, *InstallInfo], targets []string,
) (*topo.Graph[string, *InstallInfo], error) {
if graph == nil {
graph = NewGraph()
}
aurTargets := make([]string, 0, len(targets))
for _, targetString := range targets {
target := ToTarget(targetString)
switch target.DB {
case "": // unspecified db
if pkg := g.dbExecutor.SyncSatisfier(target.Name); pkg != nil {
g.GraphSyncPkg(ctx, graph, pkg, nil)
continue
}
groupPackages := g.dbExecutor.PackagesFromGroup(target.Name)
if len(groupPackages) > 0 {
dbName := groupPackages[0].DB().Name()
g.GraphSyncGroup(ctx, graph, target.Name, dbName)
continue
}
fallthrough
case "aur":
aurTargets = append(aurTargets, target.Name)
default:
pkg, err := g.dbExecutor.SatisfierFromDB(target.Name, target.DB)
if err != nil {
return nil, err
}
if pkg != nil {
g.GraphSyncPkg(ctx, graph, pkg, nil)
continue
}
groupPackages, err := g.dbExecutor.PackagesFromGroupAndDB(target.Name, target.DB)
if err != nil {
return nil, err
}
if len(groupPackages) > 0 {
g.GraphSyncGroup(ctx, graph, target.Name, target.DB)
continue
}
g.logger.Errorln(gotext.Get("No package found for"), " ", target)
}
}
var errA error
graph, errA = g.GraphFromAUR(ctx, graph, aurTargets)
if errA != nil {
return nil, errA
}
return graph, nil
}
func (g *Grapher) pickSrcInfoPkgs(pkgs []*aurc.Pkg) ([]*aurc.Pkg, error) {
final := make([]*aurc.Pkg, 0, len(pkgs))
for i := range pkgs {
g.logger.Println(text.Magenta(strconv.Itoa(i+1)+" ") + text.Bold(pkgs[i].Name) +
" " + text.Cyan(pkgs[i].Version))
g.logger.Println(" " + pkgs[i].Description)
}
g.logger.Infoln(gotext.Get("Packages to exclude") + " (eg: \"1 2 3\", \"1-3\", \"^4\"):")
numberBuf, err := g.logger.GetInput("", g.noConfirm)
if err != nil {
return nil, err
}
include, exclude, _, otherExclude := intrange.ParseNumberMenu(numberBuf)
isInclude := len(exclude) == 0 && otherExclude.Cardinality() == 0
for i := 1; i <= len(pkgs); i++ {
target := i - 1
if isInclude && !include.Get(i) {
final = append(final, pkgs[target])
}
if !isInclude && (exclude.Get(i)) {
final = append(final, pkgs[target])
}
}
return final, nil
}
func (g *Grapher) addAurPkgProvides(pkg *aurc.Pkg, graph *topo.Graph[string, *InstallInfo]) {
for i := range pkg.Provides {
depName, mod, version := splitDep(pkg.Provides[i])
g.logger.Debugln(pkg.String() + " provides: " + depName)
graph.Provides(depName, &alpm.Depend{
Name: depName,
Version: version,
Mod: aurDepModToAlpmDep(mod),
}, pkg.Name)
}
}
func (g *Grapher) GraphFromSrcInfos(ctx context.Context, graph *topo.Graph[string, *InstallInfo],
srcInfos map[string]*gosrc.Srcinfo,
) (*topo.Graph[string, *InstallInfo], error) {
if graph == nil {
graph = NewGraph()
}
aurPkgsAdded := []*aurc.Pkg{}
for pkgBuildDir, pkgbuild := range srcInfos {
pkgBuildDir := pkgBuildDir
aurPkgs, err := makeAURPKGFromSrcinfo(g.dbExecutor, pkgbuild)
if err != nil {
return nil, err
}
if len(aurPkgs) > 1 {
var errPick error
aurPkgs, errPick = g.pickSrcInfoPkgs(aurPkgs)
if errPick != nil {
return nil, errPick
}
}
for _, pkg := range aurPkgs {
pkg := pkg
reason := Explicit
if pkg := g.dbExecutor.LocalPackage(pkg.Name); pkg != nil {
reason = Reason(pkg.Reason())
}
graph.AddNode(pkg.Name)
g.addAurPkgProvides(pkg, graph)
g.ValidateAndSetNodeInfo(graph, pkg.Name, &topo.NodeInfo[*InstallInfo]{
Color: colorMap[reason],
Background: bgColorMap[AUR],
Value: &InstallInfo{
Source: SrcInfo,
Reason: reason,
SrcinfoPath: &pkgBuildDir,
AURBase: &pkg.PackageBase,
Version: pkg.Version,
},
})
}
aurPkgsAdded = append(aurPkgsAdded, aurPkgs...)
}
g.AddDepsForPkgs(ctx, aurPkgsAdded, graph)
return graph, nil
}
func (g *Grapher) AddDepsForPkgs(ctx context.Context, pkgs []*aur.Pkg, graph *topo.Graph[string, *InstallInfo]) {
for _, pkg := range pkgs {
g.addDepNodes(ctx, pkg, graph)
}
}
func (g *Grapher) addDepNodes(ctx context.Context, pkg *aur.Pkg, graph *topo.Graph[string, *InstallInfo]) {
if len(pkg.MakeDepends) > 0 {
g.addNodes(ctx, graph, pkg.Name, pkg.MakeDepends, MakeDep)
}
if !g.noDeps && len(pkg.Depends) > 0 {
g.addNodes(ctx, graph, pkg.Name, pkg.Depends, Dep)
}
if !g.noCheckDeps && !g.noDeps && len(pkg.CheckDepends) > 0 {
g.addNodes(ctx, graph, pkg.Name, pkg.CheckDepends, CheckDep)
}
}
func (g *Grapher) GraphSyncPkg(ctx context.Context,
graph *topo.Graph[string, *InstallInfo],
pkg alpm.IPackage, upgradeInfo *db.SyncUpgrade,
) *topo.Graph[string, *InstallInfo] {
if graph == nil {
graph = NewGraph()
}
graph.AddNode(pkg.Name())
_ = pkg.Provides().ForEach(func(p *alpm.Depend) error {
g.logger.Debugln(pkg.Name() + " provides: " + p.String())
graph.Provides(p.Name, p, pkg.Name())
return nil
})
dbName := pkg.DB().Name()
info := &InstallInfo{
Source: Sync,
Reason: Explicit,
Version: pkg.Version(),
SyncDBName: &dbName,
}
if upgradeInfo == nil {
if localPkg := g.dbExecutor.LocalPackage(pkg.Name()); localPkg != nil {
info.Reason = Reason(localPkg.Reason())
}
} else {
info.Upgrade = true
info.Reason = Reason(upgradeInfo.Reason)
info.LocalVersion = upgradeInfo.LocalVersion
}
g.ValidateAndSetNodeInfo(graph, pkg.Name(), &topo.NodeInfo[*InstallInfo]{
Color: colorMap[info.Reason],
Background: bgColorMap[info.Source],
Value: info,
})
return graph
}
func (g *Grapher) GraphSyncGroup(ctx context.Context,
graph *topo.Graph[string, *InstallInfo],
groupName, dbName string,
) *topo.Graph[string, *InstallInfo] {
if graph == nil {
graph = NewGraph()
}
graph.AddNode(groupName)
g.ValidateAndSetNodeInfo(graph, groupName, &topo.NodeInfo[*InstallInfo]{
Color: colorMap[Explicit],
Background: bgColorMap[Sync],
Value: &InstallInfo{
Source: Sync,
Reason: Explicit,
Version: "",
SyncDBName: &dbName,
IsGroup: true,
},
})
return graph
}
func (g *Grapher) GraphAURTarget(ctx context.Context,
graph *topo.Graph[string, *InstallInfo],
pkg *aurc.Pkg, instalInfo *InstallInfo,
) *topo.Graph[string, *InstallInfo] {
if graph == nil {
graph = NewGraph()
}
graph.AddNode(pkg.Name)
g.addAurPkgProvides(pkg, graph)
g.ValidateAndSetNodeInfo(graph, pkg.Name, &topo.NodeInfo[*InstallInfo]{
Color: colorMap[instalInfo.Reason],
Background: bgColorMap[AUR],
Value: instalInfo,
})
return graph
}
func (g *Grapher) GraphFromAUR(ctx context.Context,
graph *topo.Graph[string, *InstallInfo],
targets []string,
) (*topo.Graph[string, *InstallInfo], error) {
if graph == nil {
graph = NewGraph()
}
if len(targets) == 0 {
return graph, nil
}
aurPkgs, errCache := g.aurClient.Get(ctx, &aurc.Query{By: aurc.Name, Needles: targets})
if errCache != nil {
g.logger.Errorln(errCache)
}
for i := range aurPkgs {
pkg := &aurPkgs[i]
if _, ok := g.providerCache[pkg.Name]; !ok {
g.providerCache[pkg.Name] = []aurc.Pkg{*pkg}
}
}
aurPkgsAdded := []*aurc.Pkg{}
for _, target := range targets {
if cachedProvidePkg, ok := g.providerCache[target]; ok {
aurPkgs = cachedProvidePkg
} else {
var errA error
aurPkgs, errA = g.aurClient.Get(ctx, &aurc.Query{By: aurc.Provides, Needles: []string{target}, Contains: true})
if errA != nil {
g.logger.Errorln(gotext.Get("Failed to find AUR package for"), " ", target, ":", errA)
}
}
if len(aurPkgs) == 0 {
g.logger.Errorln(gotext.Get("No AUR package found for"), " ", target)
continue
}
aurPkg := &aurPkgs[0]
if len(aurPkgs) > 1 {
chosen := g.provideMenu(target, aurPkgs)
aurPkg = chosen
g.providerCache[target] = []aurc.Pkg{*aurPkg}
}
reason := Explicit
if pkg := g.dbExecutor.LocalPackage(aurPkg.Name); pkg != nil {
reason = Reason(pkg.Reason())
if g.needed {
if db.VerCmp(pkg.Version(), aurPkg.Version) >= 0 {
g.logger.Warnln(gotext.Get("%s is up to date -- skipping", text.Cyan(pkg.Name()+"-"+pkg.Version())))
continue
}
}
}
graph = g.GraphAURTarget(ctx, graph, aurPkg, &InstallInfo{
AURBase: &aurPkg.PackageBase,
Reason: reason,
Source: AUR,
Version: aurPkg.Version,
})
aurPkgsAdded = append(aurPkgsAdded, aurPkg)
}
g.AddDepsForPkgs(ctx, aurPkgsAdded, graph)
return graph, nil
}
// Removes found deps from the deps mapset and returns the found deps.
func (g *Grapher) findDepsFromAUR(ctx context.Context,
deps mapset.Set[string],
) []aurc.Pkg {
pkgsToAdd := make([]aurc.Pkg, 0, deps.Cardinality())
if deps.Cardinality() == 0 {
return []aurc.Pkg{}
}
missingNeedles := make([]string, 0, deps.Cardinality())
for _, depString := range deps.ToSlice() {
if _, ok := g.providerCache[depString]; !ok {
depName, _, _ := splitDep(depString)
missingNeedles = append(missingNeedles, depName)
}
}
if len(missingNeedles) != 0 {
g.logger.Debugln("deps to find", missingNeedles)
// provider search is more demanding than a simple search
// try to find name match if possible and then try to find provides.
aurPkgs, errCache := g.aurClient.Get(ctx, &aurc.Query{
By: aurc.Name, Needles: missingNeedles, Contains: false,
})
if errCache != nil {
g.logger.Errorln(errCache)
}
for i := range aurPkgs {
pkg := &aurPkgs[i]
if deps.Contains(pkg.Name) {
g.providerCache[pkg.Name] = append(g.providerCache[pkg.Name], *pkg)
}
for _, val := range pkg.Provides {
if val == pkg.Name {
continue
}
if deps.Contains(val) {
g.providerCache[val] = append(g.providerCache[val], *pkg)
}
}
}
}
for _, depString := range deps.ToSlice() {
var aurPkgs []aurc.Pkg
depName, _, _ := splitDep(depString)
if cachedProvidePkg, ok := g.providerCache[depString]; ok {
aurPkgs = cachedProvidePkg
} else {
var errA error
aurPkgs, errA = g.aurClient.Get(ctx, &aurc.Query{By: aurc.Provides, Needles: []string{depName}, Contains: true})
if errA != nil {
g.logger.Errorln(gotext.Get("Failed to find AUR package for"), depString, ":", errA)
}
}
// remove packages that don't satisfy the dependency
satisfyingPkgs := make([]aurc.Pkg, 0, len(aurPkgs))
for i := range aurPkgs {
if satisfiesAur(depString, &aurPkgs[i]) {
satisfyingPkgs = append(satisfyingPkgs, aurPkgs[i])
}
}
aurPkgs = satisfyingPkgs
if len(aurPkgs) == 0 {
g.logger.Errorln(gotext.Get("No AUR package found for"), " ", depString)
continue
}
pkg := aurPkgs[0]
if len(aurPkgs) > 1 {
chosen := g.provideMenu(depString, aurPkgs)
pkg = *chosen
}
g.providerCache[depString] = []aurc.Pkg{pkg}
deps.Remove(depString)
pkgsToAdd = append(pkgsToAdd, pkg)
}
return pkgsToAdd
}
func (g *Grapher) ValidateAndSetNodeInfo(graph *topo.Graph[string, *InstallInfo],
node string, nodeInfo *topo.NodeInfo[*InstallInfo],
) {
info := graph.GetNodeInfo(node)
if info != nil && info.Value != nil {
if info.Value.Reason < nodeInfo.Value.Reason {
return // refuse to downgrade reason
}
if info.Value.Upgrade {
return // refuse to overwrite an upgrade
}
}
graph.SetNodeInfo(node, nodeInfo)
}
func (g *Grapher) addNodes(
ctx context.Context,
graph *topo.Graph[string, *InstallInfo],
parentPkgName string,
deps []string,
depType Reason,
) {
targetsToFind := mapset.NewThreadUnsafeSet(deps...)
// Check if in graph already
for _, depString := range targetsToFind.ToSlice() {
depName, _, _ := splitDep(depString)
if !graph.Exists(depName) && !graph.ProvidesExists(depName) {
continue
}
if graph.Exists(depName) {
if err := graph.DependOn(depName, parentPkgName); err != nil {
g.logger.Warnln(depString, parentPkgName, err)
}
targetsToFind.Remove(depString)
}
if p := graph.GetProviderNode(depName); p != nil {
if provideSatisfies(p.String(), depString, p.Version) {
if err := graph.DependOn(p.Provider, parentPkgName); err != nil {
g.logger.Warnln(p.Provider, parentPkgName, err)
}
targetsToFind.Remove(depString)
}
}
}
// Check installed
for _, depString := range targetsToFind.ToSlice() {
depName, _, _ := splitDep(depString)
if !g.dbExecutor.LocalSatisfierExists(depString) {
continue
}
if g.fullGraph {
g.ValidateAndSetNodeInfo(
graph,
depName,
&topo.NodeInfo[*InstallInfo]{Color: colorMap[depType], Background: bgColorMap[Local]})
if err := graph.DependOn(depName, parentPkgName); err != nil {
g.logger.Warnln(depName, parentPkgName, err)
}
}
targetsToFind.Remove(depString)
}
// Check Sync
for _, depString := range targetsToFind.ToSlice() {
alpmPkg := g.dbExecutor.SyncSatisfier(depString)
if alpmPkg == nil {
continue
}
if err := graph.DependOn(alpmPkg.Name(), parentPkgName); err != nil {
g.logger.Warnln("repo dep warn:", depString, parentPkgName, err)
}
dbName := alpmPkg.DB().Name()
g.ValidateAndSetNodeInfo(
graph,
alpmPkg.Name(),
&topo.NodeInfo[*InstallInfo]{
Color: colorMap[depType],
Background: bgColorMap[Sync],
Value: &InstallInfo{
Source: Sync,
Reason: depType,
Version: alpmPkg.Version(),
SyncDBName: &dbName,
},
})
if newDeps := alpmPkg.Depends().Slice(); len(newDeps) != 0 && g.fullGraph {
newDepsSlice := make([]string, 0, len(newDeps))
for _, newDep := range newDeps {
newDepsSlice = append(newDepsSlice, newDep.Name)
}
g.addNodes(ctx, graph, alpmPkg.Name(), newDepsSlice, Dep)
}
targetsToFind.Remove(depString)
}
// Check AUR
pkgsToAdd := g.findDepsFromAUR(ctx, targetsToFind)
for i := range pkgsToAdd {
aurPkg := &pkgsToAdd[i]
if err := graph.DependOn(aurPkg.Name, parentPkgName); err != nil {
g.logger.Warnln("aur dep warn:", aurPkg.Name, parentPkgName, err)
}
graph.SetNodeInfo(
aurPkg.Name,
&topo.NodeInfo[*InstallInfo]{
Color: colorMap[depType],
Background: bgColorMap[AUR],
Value: &InstallInfo{
Source: AUR,
Reason: depType,
AURBase: &aurPkg.PackageBase,
Version: aurPkg.Version,
},
})
g.addDepNodes(ctx, aurPkg, graph)
}
// Add missing to graph
for _, depString := range targetsToFind.ToSlice() {
depName, mod, ver := splitDep(depString)
// no dep found. add as missing
if err := graph.DependOn(depName, parentPkgName); err != nil {
g.logger.Warnln("missing dep warn:", depString, parentPkgName, err)
}
graph.SetNodeInfo(depName, &topo.NodeInfo[*InstallInfo]{
Color: colorMap[depType],
Background: bgColorMap[Missing],
Value: &InstallInfo{
Source: Missing,
Reason: depType,
Version: fmt.Sprintf("%s%s", mod, ver),
},
})
}
}
func (g *Grapher) provideMenu(dep string, options []aur.Pkg) *aur.Pkg {
size := len(options)
if size == 1 {
return &options[0]
}
str := text.Bold(gotext.Get("There are %[1]d providers available for %[2]s:", size, dep))
str += "\n"
size = 1
str += g.logger.SprintOperationInfo(gotext.Get("Repository AUR"), "\n ")
for i := range options {
str += fmt.Sprintf("%d) %s ", size, options[i].Name)
size++
}
g.logger.OperationInfoln(str)
for {
g.logger.Println(gotext.Get("\nEnter a number (default=1): "))
if g.noConfirm {
g.logger.Println("1")
return &options[0]
}
numberBuf, err := g.logger.GetInput("", false)
if err != nil {
g.logger.Errorln(err)
return &options[0]
}
if numberBuf == "" {
return &options[0]
}
num, err := strconv.Atoi(numberBuf)
if err != nil {
g.logger.Errorln(gotext.Get("invalid number: %s", numberBuf))
continue
}
if num < 1 || num >= size {
g.logger.Errorln(gotext.Get("invalid value: %d is not between %d and %d",
num, 1, size-1))
continue
}
return &options[num-1]
}
}
func makeAURPKGFromSrcinfo(dbExecutor db.Executor, srcInfo *gosrc.Srcinfo) ([]*aur.Pkg, error) {
pkgs := make([]*aur.Pkg, 0, 1)
alpmArch, err := dbExecutor.AlpmArchitectures()
if err != nil {
return nil, err
}
alpmArch = append(alpmArch, "") // srcinfo assumes no value as ""
getDesc := func(pkg *gosrc.Package) string {
if pkg.Pkgdesc != "" {
return pkg.Pkgdesc
}
return srcInfo.Pkgdesc
}
for i := range srcInfo.Packages {
pkg := &srcInfo.Packages[i]
pkgs = append(pkgs, &aur.Pkg{
ID: 0,
Name: pkg.Pkgname,
PackageBaseID: 0,
PackageBase: srcInfo.Pkgbase,
Version: srcInfo.Version(),
Description: getDesc(pkg),
URL: pkg.URL,
Depends: append(archStringToString(alpmArch, pkg.Depends),
archStringToString(alpmArch, srcInfo.Depends)...),
MakeDepends: archStringToString(alpmArch, srcInfo.MakeDepends),
CheckDepends: archStringToString(alpmArch, srcInfo.CheckDepends),
Conflicts: append(archStringToString(alpmArch, pkg.Conflicts),
archStringToString(alpmArch, srcInfo.Conflicts)...),
Provides: append(archStringToString(alpmArch, pkg.Provides),
archStringToString(alpmArch, srcInfo.Provides)...),
Replaces: append(archStringToString(alpmArch, pkg.Replaces),
archStringToString(alpmArch, srcInfo.Replaces)...),
OptDepends: append(archStringToString(alpmArch, pkg.OptDepends),
archStringToString(alpmArch, srcInfo.OptDepends)...),
Groups: pkg.Groups,
License: pkg.License,
Keywords: []string{},
})
}
return pkgs, nil
}
func archStringToString(alpmArches []string, archString []gosrc.ArchString) []string {
pkgs := make([]string, 0, len(archString))
for _, arch := range archString {
if db.ArchIsSupported(alpmArches, arch.Arch) {
pkgs = append(pkgs, arch.Value)
}
}
return pkgs
}
func aurDepModToAlpmDep(mod string) alpm.DepMod {
switch mod {
case "=":
return alpm.DepModEq
case ">=":
return alpm.DepModGE
case "<=":
return alpm.DepModLE
case ">":
return alpm.DepModGT
case "<":
return alpm.DepModLT
}
return alpm.DepModAny
}

811
pkg/dep/dep_graph_test.go Normal file
View File

@ -0,0 +1,811 @@
//go:build !integration
// +build !integration
package dep
import (
"context"
"encoding/json"
"fmt"
"io"
"os"
"testing"
aurc "github.com/Jguer/aur"
alpm "github.com/Jguer/go-alpm/v2"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/db/mock"
mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
aur "github.com/Jguer/yay/v12/pkg/query"
"github.com/Jguer/yay/v12/pkg/text"
)
func ptrString(s string) *string {
return &s
}
func getFromFile(t *testing.T, filePath string) mockaur.GetFunc {
f, err := os.Open(filePath)
require.NoError(t, err)
fBytes, err := io.ReadAll(f)
require.NoError(t, err)
pkgs := []aur.Pkg{}
err = json.Unmarshal(fBytes, &pkgs)
require.NoError(t, err)
return func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
return pkgs, nil
}
}
func TestGrapher_GraphFromTargets_jellyfin(t *testing.T) {
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
SyncSatisfierFn: func(s string) mock.IPackage {
switch s {
case "jellyfin":
return nil
case "dotnet-runtime-6.0":
return &mock.Package{
PName: "dotnet-runtime-6.0",
PBase: "dotnet-runtime-6.0",
PVersion: "6.0.100-1",
PDB: mock.NewDB("community"),
}
case "dotnet-sdk-6.0":
return &mock.Package{
PName: "dotnet-sdk-6.0",
PBase: "dotnet-sdk-6.0",
PVersion: "6.0.100-1",
PDB: mock.NewDB("community"),
}
}
return nil
},
PackagesFromGroupFn: func(string) []mock.IPackage { return nil },
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "dotnet-sdk-6.0", "dotnet-runtime-6.0", "jellyfin-server=10.8.8", "jellyfin-web=10.8.8":
return false
}
return true
},
LocalPackageFn: func(string) mock.IPackage { return nil },
}
mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
if query.Needles[0] == "jellyfin" {
jfinFn := getFromFile(t, "testdata/jellyfin.json")
return jfinFn(ctx, query)
}
if query.Needles[0] == "jellyfin-web" {
jfinWebFn := getFromFile(t, "testdata/jellyfin-web.json")
return jfinWebFn(ctx, query)
}
if query.Needles[0] == "jellyfin-server" {
jfinServerFn := getFromFile(t, "testdata/jellyfin-server.json")
return jfinServerFn(ctx, query)
}
panic(fmt.Sprintf("implement me %v", query.Needles))
}}
type fields struct {
dbExecutor db.Executor
aurCache aurc.QueryClient
noDeps bool
noCheckDeps bool
}
type args struct {
targets []string
}
tests := []struct {
name string
fields fields
args args
want []map[string]*InstallInfo
wantErr bool
}{
{
name: "noDeps",
fields: fields{
dbExecutor: mockDB,
aurCache: mockAUR,
noDeps: true,
noCheckDeps: false,
},
args: args{
targets: []string{"jellyfin"},
},
want: []map[string]*InstallInfo{
{
"jellyfin": {
Source: AUR,
Reason: Explicit,
Version: "10.8.8-1",
AURBase: ptrString("jellyfin"),
},
},
{
"dotnet-sdk-6.0": {
Source: Sync,
Reason: MakeDep,
Version: "6.0.100-1",
SyncDBName: ptrString("community"),
},
},
},
wantErr: false,
},
{
name: "deps",
fields: fields{
dbExecutor: mockDB,
aurCache: mockAUR,
noDeps: false,
noCheckDeps: false,
},
args: args{
targets: []string{"jellyfin"},
},
want: []map[string]*InstallInfo{
{
"jellyfin": {
Source: AUR,
Reason: Explicit,
Version: "10.8.8-1",
AURBase: ptrString("jellyfin"),
},
},
{
"jellyfin-web": {
Source: AUR,
Reason: Dep,
Version: "10.8.8-1",
AURBase: ptrString("jellyfin"),
},
"jellyfin-server": {
Source: AUR,
Reason: Dep,
Version: "10.8.8-1",
AURBase: ptrString("jellyfin"),
},
},
{
"dotnet-sdk-6.0": {
Source: Sync,
Reason: MakeDep,
Version: "6.0.100-1",
SyncDBName: ptrString("community"),
},
"dotnet-runtime-6.0": {
Source: Sync,
Reason: Dep,
Version: "6.0.100-1",
SyncDBName: ptrString("community"),
},
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewGrapher(tt.fields.dbExecutor,
tt.fields.aurCache, false, true,
tt.fields.noDeps, tt.fields.noCheckDeps, false,
text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test"))
got, err := g.GraphFromTargets(context.Background(), nil, tt.args.targets)
require.NoError(t, err)
layers := got.TopoSortedLayerMap(nil)
require.EqualValues(t, tt.want, layers, layers)
})
}
}
func TestGrapher_GraphProvides_androidsdk(t *testing.T) {
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
SyncSatisfierFn: func(s string) mock.IPackage {
switch s {
case "android-sdk":
return nil
case "jdk11-openjdk":
return &mock.Package{
PName: "jdk11-openjdk",
PVersion: "11.0.12.u7-1",
PDB: mock.NewDB("community"),
PProvides: mock.DependList{
Depends: []alpm.Depend{
{Name: "java-environment", Version: "11", Mod: alpm.DepModEq},
{Name: "java-environment-openjdk", Version: "11", Mod: alpm.DepModEq},
{Name: "jdk11-openjdk", Version: "11.0.19.u7-1", Mod: alpm.DepModEq},
},
},
}
case "java-environment":
panic("not supposed to be called")
}
panic("implement me " + s)
},
PackagesFromGroupFn: func(string) []mock.IPackage { return nil },
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "java-environment":
return false
}
switch s {
case "libxtst", "fontconfig", "freetype2", "lib32-gcc-libs", "lib32-glibc", "libx11", "libxext", "libxrender", "zlib", "gcc-libs":
return true
}
panic("implement me " + s)
},
LocalPackageFn: func(string) mock.IPackage { return nil },
}
mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
if query.Needles[0] == "android-sdk" {
jfinFn := getFromFile(t, "testdata/android-sdk.json")
return jfinFn(ctx, query)
}
panic(fmt.Sprintf("implement me %v", query.Needles))
}}
type fields struct {
dbExecutor db.Executor
aurCache aurc.QueryClient
noDeps bool
noCheckDeps bool
}
type args struct {
targets []string
}
tests := []struct {
name string
fields fields
args args
want []map[string]*InstallInfo
wantErr bool
}{
{
name: "explicit dep",
fields: fields{
dbExecutor: mockDB,
aurCache: mockAUR,
noDeps: false,
noCheckDeps: false,
},
args: args{
targets: []string{"android-sdk", "jdk11-openjdk"},
},
want: []map[string]*InstallInfo{
{
"android-sdk": {
Source: AUR,
Reason: Explicit,
Version: "26.1.1-2",
AURBase: ptrString("android-sdk"),
},
},
{
"jdk11-openjdk": {
Source: Sync,
Reason: Explicit,
Version: "11.0.12.u7-1",
SyncDBName: ptrString("community"),
},
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewGrapher(tt.fields.dbExecutor,
tt.fields.aurCache, false, true,
tt.fields.noDeps, tt.fields.noCheckDeps, false,
text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test"))
got, err := g.GraphFromTargets(context.Background(), nil, tt.args.targets)
require.NoError(t, err)
layers := got.TopoSortedLayerMap(nil)
require.EqualValues(t, tt.want, layers, layers)
})
}
}
func TestGrapher_GraphFromAUR_Deps_ceph_bin(t *testing.T) {
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
SyncSatisfierFn: func(s string) mock.IPackage {
switch s {
case "ceph-bin", "ceph-libs-bin":
return nil
case "ceph", "ceph-libs", "ceph-libs=17.2.6-2":
return nil
}
panic("implement me " + s)
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "ceph-libs", "ceph-libs=17.2.6-2":
return false
case "dep1", "dep2", "dep3", "makedep1", "makedep2", "checkdep1":
return true
}
panic("implement me " + s)
},
LocalPackageFn: func(string) mock.IPackage { return nil },
}
mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
mockPkgs := map[string]aur.Pkg{
"ceph-bin": {
Name: "ceph-bin",
PackageBase: "ceph-bin",
Version: "17.2.6-2",
Depends: []string{"ceph-libs=17.2.6-2", "dep1"},
Provides: []string{"ceph=17.2.6-2"},
},
"ceph-libs-bin": {
Name: "ceph-libs-bin",
PackageBase: "ceph-bin",
Version: "17.2.6-2",
Depends: []string{"dep1", "dep2"},
Provides: []string{"ceph-libs=17.2.6-2"},
},
"ceph": {
Name: "ceph",
PackageBase: "ceph",
Version: "17.2.6-2",
Depends: []string{"ceph-libs=17.2.6-2", "dep1"},
MakeDepends: []string{"makedep1"},
CheckDepends: []string{"checkdep1"},
Provides: []string{"ceph=17.2.6-2"},
},
"ceph-libs": {
Name: "ceph-libs",
PackageBase: "ceph",
Version: "17.2.6-2",
Depends: []string{"dep1", "dep2", "dep3"},
MakeDepends: []string{"makedep1", "makedep2"},
CheckDepends: []string{"checkdep1"},
Provides: []string{"ceph-libs=17.2.6-2"},
},
}
pkgs := []aur.Pkg{}
for _, needle := range query.Needles {
if pkg, ok := mockPkgs[needle]; ok {
pkgs = append(pkgs, pkg)
} else {
panic(fmt.Sprintf("implement me %v", needle))
}
}
return pkgs, nil
}}
installInfos := map[string]*InstallInfo{
"ceph-bin exp": {
Source: AUR,
Reason: Explicit,
Version: "17.2.6-2",
AURBase: ptrString("ceph-bin"),
},
"ceph-libs-bin exp": {
Source: AUR,
Reason: Explicit,
Version: "17.2.6-2",
AURBase: ptrString("ceph-bin"),
},
"ceph exp": {
Source: AUR,
Reason: Explicit,
Version: "17.2.6-2",
AURBase: ptrString("ceph"),
},
"ceph-libs exp": {
Source: AUR,
Reason: Explicit,
Version: "17.2.6-2",
AURBase: ptrString("ceph"),
},
"ceph-libs dep": {
Source: AUR,
Reason: Dep,
Version: "17.2.6-2",
AURBase: ptrString("ceph"),
},
}
tests := []struct {
name string
targets []string
wantLayers []map[string]*InstallInfo
wantErr bool
}{
{
name: "ceph-bin ceph-libs-bin",
targets: []string{"ceph-bin", "ceph-libs-bin"},
wantLayers: []map[string]*InstallInfo{
{"ceph-bin": installInfos["ceph-bin exp"]},
{"ceph-libs-bin": installInfos["ceph-libs-bin exp"]},
},
wantErr: false,
},
{
name: "ceph-libs-bin ceph-bin (reversed order)",
targets: []string{"ceph-libs-bin", "ceph-bin"},
wantLayers: []map[string]*InstallInfo{
{"ceph-bin": installInfos["ceph-bin exp"]},
{"ceph-libs-bin": installInfos["ceph-libs-bin exp"]},
},
wantErr: false,
},
{
name: "ceph",
targets: []string{"ceph"},
wantLayers: []map[string]*InstallInfo{
{"ceph": installInfos["ceph exp"]},
{"ceph-libs": installInfos["ceph-libs dep"]},
},
wantErr: false,
},
{
name: "ceph-bin",
targets: []string{"ceph-bin"},
wantLayers: []map[string]*InstallInfo{
{"ceph-bin": installInfos["ceph-bin exp"]},
{"ceph-libs": installInfos["ceph-libs dep"]},
},
wantErr: false,
},
{
name: "ceph-bin ceph-libs",
targets: []string{"ceph-bin", "ceph-libs"},
wantLayers: []map[string]*InstallInfo{
{"ceph-bin": installInfos["ceph-bin exp"]},
{"ceph-libs": installInfos["ceph-libs exp"]},
},
wantErr: false,
},
{
name: "ceph-libs ceph-bin (reversed order)",
targets: []string{"ceph-libs", "ceph-bin"},
wantLayers: []map[string]*InstallInfo{
{"ceph-bin": installInfos["ceph-bin exp"]},
{"ceph-libs": installInfos["ceph-libs exp"]},
},
wantErr: false,
},
{
name: "ceph ceph-libs-bin",
targets: []string{"ceph", "ceph-libs-bin"},
wantLayers: []map[string]*InstallInfo{
{"ceph": installInfos["ceph exp"]},
{"ceph-libs-bin": installInfos["ceph-libs-bin exp"]},
},
wantErr: false,
},
{
name: "ceph-libs-bin ceph (reversed order)",
targets: []string{"ceph-libs-bin", "ceph"},
wantLayers: []map[string]*InstallInfo{
{"ceph": installInfos["ceph exp"]},
{"ceph-libs-bin": installInfos["ceph-libs-bin exp"]},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewGrapher(mockDB, mockAUR,
false, true, false, false, false,
text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test"))
got, err := g.GraphFromTargets(context.Background(), nil, tt.targets)
require.NoError(t, err)
layers := got.TopoSortedLayerMap(nil)
require.EqualValues(t, tt.wantLayers, layers, layers)
})
}
}
func TestGrapher_GraphFromAUR_Deps_gourou(t *testing.T) {
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
SyncSatisfierFn: func(s string) mock.IPackage {
switch s {
case "gourou", "libzip-git":
return nil
case "libzip":
return &mock.Package{
PName: "libzip",
PVersion: "1.9.2-1",
PDB: mock.NewDB("extra"),
}
}
panic("implement me " + s)
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "gourou", "libzip", "libzip-git":
return false
case "dep1", "dep2":
return true
}
panic("implement me " + s)
},
LocalPackageFn: func(string) mock.IPackage { return nil },
}
mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
mockPkgs := map[string]aur.Pkg{
"gourou": {
Name: "gourou",
PackageBase: "gourou",
Version: "0.8.1",
Depends: []string{"libzip"},
},
"libzip-git": {
Name: "libzip-git",
PackageBase: "libzip-git",
Version: "1.9.2.r159.gb3ac716c-1",
Depends: []string{"dep1", "dep2"},
Provides: []string{"libzip=1.9.2.r159.gb3ac716c"},
},
}
pkgs := []aur.Pkg{}
for _, needle := range query.Needles {
if pkg, ok := mockPkgs[needle]; ok {
pkgs = append(pkgs, pkg)
} else {
panic(fmt.Sprintf("implement me %v", needle))
}
}
return pkgs, nil
}}
installInfos := map[string]*InstallInfo{
"gourou exp": {
Source: AUR,
Reason: Explicit,
Version: "0.8.1",
AURBase: ptrString("gourou"),
},
"libzip dep": {
Source: Sync,
Reason: Dep,
Version: "1.9.2-1",
SyncDBName: ptrString("extra"),
},
"libzip exp": {
Source: Sync,
Reason: Explicit,
Version: "1.9.2-1",
SyncDBName: ptrString("extra"),
},
"libzip-git exp": {
Source: AUR,
Reason: Explicit,
Version: "1.9.2.r159.gb3ac716c-1",
AURBase: ptrString("libzip-git"),
},
}
tests := []struct {
name string
targets []string
wantLayers []map[string]*InstallInfo
wantErr bool
}{
{
name: "gourou",
targets: []string{"gourou"},
wantLayers: []map[string]*InstallInfo{
{"gourou": installInfos["gourou exp"]},
{"libzip": installInfos["libzip dep"]},
},
wantErr: false,
},
{
name: "gourou libzip",
targets: []string{"gourou", "libzip"},
wantLayers: []map[string]*InstallInfo{
{"gourou": installInfos["gourou exp"]},
{"libzip": installInfos["libzip exp"]},
},
wantErr: false,
},
{
name: "gourou libzip-git",
targets: []string{"gourou", "libzip-git"},
wantLayers: []map[string]*InstallInfo{
{"gourou": installInfos["gourou exp"]},
{"libzip-git": installInfos["libzip-git exp"]},
},
wantErr: false,
},
{
name: "libzip-git gourou (reversed order)",
targets: []string{"libzip-git", "gourou"},
wantLayers: []map[string]*InstallInfo{
{"gourou": installInfos["gourou exp"]},
{"libzip-git": installInfos["libzip-git exp"]},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewGrapher(mockDB, mockAUR,
false, true, false, false, false,
text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test"))
got, err := g.GraphFromTargets(context.Background(), nil, tt.targets)
require.NoError(t, err)
layers := got.TopoSortedLayerMap(nil)
require.EqualValues(t, tt.wantLayers, layers, layers)
})
}
}
func TestGrapher_GraphFromTargets_ReinstalledDeps(t *testing.T) {
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
SyncSatisfierFn: func(s string) mock.IPackage {
switch s {
case "gourou":
return nil
case "libzip":
return &mock.Package{
PName: "libzip",
PVersion: "1.9.2-1",
PDB: mock.NewDB("extra"),
}
}
panic("implement me " + s)
},
SatisfierFromDBFn: func(s, s2 string) (mock.IPackage, error) {
if s2 == "extra" {
switch s {
case "libzip":
return &mock.Package{
PName: "libzip",
PVersion: "1.9.2-1",
PDB: mock.NewDB("extra"),
}, nil
}
}
panic("implement me " + s2 + "/" + s)
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "gourou", "libzip":
return true
}
panic("implement me " + s)
},
LocalPackageFn: func(s string) mock.IPackage {
switch s {
case "libzip":
return &mock.Package{
PName: "libzip",
PVersion: "1.9.2-1",
PDB: mock.NewDB("extra"),
PReason: alpm.PkgReasonDepend,
}
case "gourou":
return &mock.Package{
PName: "gourou",
PVersion: "0.8.1",
PDB: mock.NewDB("aur"),
PReason: alpm.PkgReasonDepend,
}
}
return nil
},
}
mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
mockPkgs := map[string]aur.Pkg{
"gourou": {
Name: "gourou",
PackageBase: "gourou",
Version: "0.8.1",
Depends: []string{"libzip"},
},
}
pkgs := []aur.Pkg{}
for _, needle := range query.Needles {
if pkg, ok := mockPkgs[needle]; ok {
pkgs = append(pkgs, pkg)
} else {
panic(fmt.Sprintf("implement me %v", needle))
}
}
return pkgs, nil
}}
installInfos := map[string]*InstallInfo{
"gourou dep": {
Source: AUR,
Reason: Dep,
Version: "0.8.1",
AURBase: ptrString("gourou"),
},
"libzip dep": {
Source: Sync,
Reason: Dep,
Version: "1.9.2-1",
SyncDBName: ptrString("extra"),
},
}
tests := []struct {
name string
targets []string
wantLayers []map[string]*InstallInfo
wantErr bool
}{
{
name: "gourou libzip",
targets: []string{"gourou", "libzip"},
wantLayers: []map[string]*InstallInfo{
{"gourou": installInfos["gourou dep"]},
{"libzip": installInfos["libzip dep"]},
},
wantErr: false,
},
{
name: "aur/gourou extra/libzip",
targets: []string{"aur/gourou", "extra/libzip"},
wantLayers: []map[string]*InstallInfo{
{"gourou": installInfos["gourou dep"]},
{"libzip": installInfos["libzip dep"]},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewGrapher(mockDB, mockAUR,
false, true, false, false, false,
text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test"))
got, err := g.GraphFromTargets(context.Background(), nil, tt.targets)
require.NoError(t, err)
layers := got.TopoSortedLayerMap(nil)
require.EqualValues(t, tt.wantLayers, layers, layers)
})
}
}

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

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

34
pkg/dep/target_handler.go Normal file
View File

@ -0,0 +1,34 @@
package dep
import "github.com/Jguer/yay/v12/pkg/text"
type Target struct {
DB string
Name string
Mod string
Version string
}
func ToTarget(pkg string) Target {
dbName, depString := text.SplitDBFromName(pkg)
name, mod, depVersion := splitDep(depString)
return Target{
DB: dbName,
Name: name,
Mod: mod,
Version: depVersion,
}
}
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()
}

3
pkg/dep/testdata/android-sdk.json vendored Normal file
View File

@ -0,0 +1,3 @@
[
{"ID":1055234,"Name":"android-sdk","PackageBaseID":13751,"PackageBase":"android-sdk","Version":"26.1.1-2","Description":"Google Android SDK","URL":"https://developer.android.com/studio/releases/sdk-tools.html","NumVotes":1487,"Popularity":0.802316,"OutOfDate":null,"Maintainer":"dreamingincode","Submitter":null,"FirstSubmitted":1194895596,"LastModified":1647982720,"URLPath":"/cgit/aur.git/snapshot/android-sdk.tar.gz","Depends":["java-environment","libxtst","fontconfig","freetype2","lib32-gcc-libs","lib32-glibc","libx11","libxext","libxrender","zlib","gcc-libs"],"OptDepends":["android-emulator","android-sdk-platform-tools","android-udev"],"License":["custom"],"Keywords":["android","development"]}
]

3
pkg/dep/testdata/jellyfin-server.json vendored Normal file
View File

@ -0,0 +1,3 @@
[
{"ID":1176791,"Name":"jellyfin-server","PackageBaseID":138631,"PackageBase":"jellyfin","Version":"10.8.8-1","Description":"Jellyfin server component","URL":"https://github.com/jellyfin/jellyfin","NumVotes":84,"Popularity":1.272964,"OutOfDate":null,"Maintainer":"z3ntu","Submitter":"z3ntu","FirstSubmitted":1547053171,"LastModified":1669830147,"URLPath":"/cgit/aur.git/snapshot/jellyfin-server.tar.gz","Depends":["dotnet-runtime-6.0","aspnet-runtime-6.0","ffmpeg","sqlite"],"MakeDepends":["dotnet-sdk-6.0","nodejs","npm","git"],"License":["GPL2"]}
]

3
pkg/dep/testdata/jellyfin-web.json vendored Normal file
View File

@ -0,0 +1,3 @@
[
{"ID":1176790,"Name":"jellyfin-web","PackageBaseID":138631,"PackageBase":"jellyfin","Version":"10.8.8-1","Description":"Jellyfin web client","URL":"https://github.com/jellyfin/jellyfin","NumVotes":84,"Popularity":1.272964,"OutOfDate":null,"Maintainer":"z3ntu","Submitter":"z3ntu","FirstSubmitted":1547053171,"LastModified":1669830147,"URLPath":"/cgit/aur.git/snapshot/jellyfin-web.tar.gz","MakeDepends":["dotnet-sdk-6.0","nodejs","npm","git"],"License":["GPL2"]}
]

3
pkg/dep/testdata/jellyfin.json vendored Normal file
View File

@ -0,0 +1,3 @@
[
{"ID":1176789,"Name":"jellyfin","PackageBaseID":138631,"PackageBase":"jellyfin","Version":"10.8.8-1","Description":"The Free Software Media System","URL":"https://github.com/jellyfin/jellyfin","NumVotes":84,"Popularity":1.272964,"OutOfDate":null,"Maintainer":"z3ntu","Submitter":"z3ntu","FirstSubmitted":1547053171,"LastModified":1669830147,"URLPath":"/cgit/aur.git/snapshot/jellyfin.tar.gz","Depends":["jellyfin-web=10.8.8","jellyfin-server=10.8.8"],"MakeDepends":["dotnet-sdk-6.0","nodejs","npm","git"],"License":["GPL2"]}
]

371
pkg/dep/topo/dep.go Normal file
View File

@ -0,0 +1,371 @@
package topo
import (
"fmt"
"strings"
"github.com/Jguer/go-alpm/v2"
)
type (
NodeSet[T comparable] map[T]bool
ProvidesMap[T comparable] map[T]*DependencyInfo[T]
DepMap[T comparable] map[T]NodeSet[T]
)
func (n NodeSet[T]) Slice() []T {
var slice []T
for node := range n {
slice = append(slice, node)
}
return slice
}
type NodeInfo[V any] struct {
Color string
Background string
Value V
}
type DependencyInfo[T comparable] struct {
Provider T
alpm.Depend
}
type CheckFn[T comparable, V any] func(T, V) error
type Graph[T comparable, V any] struct {
nodes NodeSet[T]
// node info map
nodeInfo map[T]*NodeInfo[V]
// `provides` tracks provides -> node.
provides ProvidesMap[T]
// `dependencies` tracks child -> parents.
dependencies DepMap[T]
// `dependents` tracks parent -> children.
dependents DepMap[T]
}
func New[T comparable, V any]() *Graph[T, V] {
return &Graph[T, V]{
nodes: make(NodeSet[T]),
dependencies: make(DepMap[T]),
dependents: make(DepMap[T]),
nodeInfo: make(map[T]*NodeInfo[V]),
provides: make(ProvidesMap[T]),
}
}
func (g *Graph[T, V]) Len() int {
return len(g.nodes)
}
func (g *Graph[T, V]) Exists(node T) bool {
_, ok := g.nodes[node]
return ok
}
func (g *Graph[T, V]) AddNode(node T) {
g.nodes[node] = true
}
func (g *Graph[T, V]) ProvidesExists(provides T) bool {
_, ok := g.provides[provides]
return ok
}
func (g *Graph[T, V]) GetProviderNode(provides T) *DependencyInfo[T] {
return g.provides[provides]
}
func (g *Graph[T, V]) Provides(provides T, depInfo *alpm.Depend, node T) {
g.provides[provides] = &DependencyInfo[T]{
Provider: node,
Depend: *depInfo,
}
}
func (g *Graph[T, V]) ForEach(f CheckFn[T, V]) error {
for node := range g.nodes {
if err := f(node, g.nodeInfo[node].Value); err != nil {
return err
}
}
return nil
}
func (g *Graph[T, V]) SetNodeInfo(node T, nodeInfo *NodeInfo[V]) {
g.nodeInfo[node] = nodeInfo
}
func (g *Graph[T, V]) GetNodeInfo(node T) *NodeInfo[V] {
return g.nodeInfo[node]
}
func (g *Graph[T, V]) DependOn(child, parent T) error {
if child == parent {
return ErrSelfReferential
}
if g.DependsOn(parent, child) {
return ErrCircular
}
g.AddNode(parent)
g.AddNode(child)
// Add edges.
g.dependents.addNodeToNodeset(parent, child)
g.dependencies.addNodeToNodeset(child, parent)
return nil
}
func (g *Graph[T, V]) String() string {
var sb strings.Builder
sb.WriteString("digraph {\n")
sb.WriteString("compound=true;\n")
sb.WriteString("concentrate=true;\n")
sb.WriteString("node [shape = record, ordering=out];\n")
for node := range g.nodes {
extra := ""
if info, ok := g.nodeInfo[node]; ok {
if info.Background != "" || info.Color != "" {
extra = fmt.Sprintf("[color = %s, style = filled, fillcolor = %s]", info.Color, info.Background)
}
}
sb.WriteString(fmt.Sprintf("\t\"%v\"%s;\n", node, extra))
}
for parent, children := range g.dependencies {
for child := range children {
sb.WriteString(fmt.Sprintf("\t\"%v\" -> \"%v\";\n", parent, child))
}
}
sb.WriteString("}")
return sb.String()
}
func (g *Graph[T, V]) DependsOn(child, parent T) bool {
deps := g.Dependencies(child)
_, ok := deps[parent]
return ok
}
func (g *Graph[T, V]) HasDependent(parent, child T) bool {
deps := g.Dependents(parent)
_, ok := deps[child]
return ok
}
// leavesMap returns a map of leaves with the node as key and the node info value as value.
func (g *Graph[T, V]) leavesMap() map[T]V {
leaves := make(map[T]V, 0)
for node := range g.nodes {
if _, ok := g.dependencies[node]; !ok {
nodeInfo := g.GetNodeInfo(node)
if nodeInfo == nil {
nodeInfo = &NodeInfo[V]{}
}
leaves[node] = nodeInfo.Value
}
}
return leaves
}
// TopoSortedLayerMap returns a slice of all of the graph nodes in topological sort order with their node info.
func (g *Graph[T, V]) TopoSortedLayerMap(checkFn CheckFn[T, V]) []map[T]V {
layers := []map[T]V{}
// Copy the graph
shrinkingGraph := g.clone()
for {
leaves := shrinkingGraph.leavesMap()
if len(leaves) == 0 {
break
}
layers = append(layers, leaves)
for leafNode := range leaves {
if checkFn != nil {
if err := checkFn(leafNode, leaves[leafNode]); err != nil {
return nil
}
}
shrinkingGraph.remove(leafNode)
}
}
return layers
}
// returns if it was the last
func (dm DepMap[T]) removeFromDepmap(key, node T) bool {
if nodes := dm[key]; len(nodes) == 1 {
// The only element in the nodeset must be `node`, so we
// can delete the entry entirely.
delete(dm, key)
return true
} else {
// Otherwise, remove the single node from the nodeset.
delete(nodes, node)
return false
}
}
// Prune removes the node,
// its dependencies if there are no other dependents
// and its dependents
func (g *Graph[T, V]) Prune(node T) []T {
pruned := []T{node}
// Remove edges from things that depend on `node`.
for dependent := range g.dependents[node] {
last := g.dependencies.removeFromDepmap(dependent, node)
if last {
pruned = append(pruned, g.Prune(dependent)...)
}
}
delete(g.dependents, node)
// Remove all edges from node to the things it depends on.
for dependency := range g.dependencies[node] {
last := g.dependents.removeFromDepmap(dependency, node)
if last {
pruned = append(pruned, g.Prune(dependency)...)
}
}
delete(g.dependencies, node)
// Finally, remove the node itself.
delete(g.nodes, node)
return pruned
}
func (g *Graph[T, V]) remove(node T) {
// Remove edges from things that depend on `node`.
for dependent := range g.dependents[node] {
g.dependencies.removeFromDepmap(dependent, node)
}
delete(g.dependents, node)
// Remove all edges from node to the things it depends on.
for dependency := range g.dependencies[node] {
g.dependents.removeFromDepmap(dependency, node)
}
delete(g.dependencies, node)
// Finally, remove the node itself.
delete(g.nodes, node)
}
func (g *Graph[T, V]) Dependencies(child T) NodeSet[T] {
return g.buildTransitive(child, g.ImmediateDependencies)
}
func (g *Graph[T, V]) ImmediateDependencies(node T) NodeSet[T] {
return g.dependencies[node]
}
func (g *Graph[T, V]) Dependents(parent T) NodeSet[T] {
return g.buildTransitive(parent, g.immediateDependents)
}
func (g *Graph[T, V]) immediateDependents(node T) NodeSet[T] {
return g.dependents[node]
}
func (g *Graph[T, V]) clone() *Graph[T, V] {
return &Graph[T, V]{
dependencies: g.dependencies.copy(),
dependents: g.dependents.copy(),
nodes: g.nodes.copy(),
nodeInfo: g.nodeInfo, // not copied, as it is not modified
}
}
// buildTransitive starts at `root` and continues calling `nextFn` to keep discovering more nodes until
// the graph cannot produce any more. It returns the set of all discovered nodes.
func (g *Graph[T, V]) buildTransitive(root T, nextFn func(T) NodeSet[T]) NodeSet[T] {
if _, ok := g.nodes[root]; !ok {
return nil
}
out := make(NodeSet[T])
searchNext := []T{root}
for len(searchNext) > 0 {
// List of new nodes from this layer of the dependency graph. This is
// assigned to `searchNext` at the end of the outer "discovery" loop.
discovered := []T{}
for _, node := range searchNext {
// For each node to discover, find the next nodes.
for nextNode := range nextFn(node) {
// If we have not seen the node before, add it to the output as well
// as the list of nodes to traverse in the next iteration.
if _, ok := out[nextNode]; !ok {
out[nextNode] = true
discovered = append(discovered, nextNode)
}
}
}
searchNext = discovered
}
return out
}
func (s NodeSet[T]) copy() NodeSet[T] {
out := make(NodeSet[T], len(s))
for k, v := range s {
out[k] = v
}
return out
}
func (dm DepMap[T]) copy() DepMap[T] {
out := make(DepMap[T], len(dm))
for k := range dm {
out[k] = dm[k].copy()
}
return out
}
func (dm DepMap[T]) addNodeToNodeset(key, node T) {
nodes, ok := dm[key]
if !ok {
nodes = make(NodeSet[T])
dm[key] = nodes
}
nodes[node] = true
}

9
pkg/dep/topo/errors.go Normal file
View File

@ -0,0 +1,9 @@
package topo
import "errors"
var (
ErrSelfReferential = errors.New(" self-referential dependencies not allowed")
ErrConflictingAlias = errors.New(" alias already defined")
ErrCircular = errors.New(" circular dependencies not allowed")
)

92
pkg/download/abs.go Normal file
View File

@ -0,0 +1,92 @@
package download
import (
"context"
"errors"
"fmt"
"io"
"net/http"
"regexp"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/settings/exe"
)
const (
MaxConcurrentFetch = 20
absPackageURL = "https://gitlab.archlinux.org/archlinux/packaging/packages"
)
var (
ErrInvalidRepository = errors.New(gotext.Get("invalid repository"))
ErrABSPackageNotFound = errors.New(gotext.Get("package not found in repos"))
)
type regexReplace struct {
repl string
match *regexp.Regexp
}
// regex replacements for Gitlab URLs
// info: https://gitlab.archlinux.org/archlinux/devtools/-/blob/6ce666a1669235749c17d5c44d8a24dea4a135da/src/lib/api/gitlab.sh#L84
var gitlabRepl = []regexReplace{
{repl: `$1-$2`, match: regexp.MustCompile(`([a-zA-Z0-9]+)\+([a-zA-Z]+)`)},
{repl: `plus`, match: regexp.MustCompile(`\+`)},
{repl: `-`, match: regexp.MustCompile(`[^a-zA-Z0-9_\-.]`)},
{repl: `-`, match: regexp.MustCompile(`[_\-]{2,}`)},
{repl: `unix-tree`, match: regexp.MustCompile(`^tree$`)},
}
// Return format for pkgbuild
// https://gitlab.archlinux.org/archlinux/packaging/packages/0ad/-/raw/main/PKGBUILD
func getPackagePKGBUILDURL(pkgName string) string {
return fmt.Sprintf("%s/%s/-/raw/main/PKGBUILD", absPackageURL, convertPkgNameForURL(pkgName))
}
// Return format for pkgbuild repo
// https://gitlab.archlinux.org/archlinux/packaging/packages/0ad.git
func getPackageRepoURL(pkgName string) string {
return fmt.Sprintf("%s/%s.git", absPackageURL, convertPkgNameForURL(pkgName))
}
// convert pkgName for Gitlab URL path (repo name)
func convertPkgNameForURL(pkgName string) string {
for _, regex := range gitlabRepl {
pkgName = regex.match.ReplaceAllString(pkgName, regex.repl)
}
return pkgName
}
// ABSPKGBUILD retrieves the PKGBUILD file to a dest directory.
func ABSPKGBUILD(httpClient httpRequestDoer, dbName, pkgName string) ([]byte, error) {
packageURL := getPackagePKGBUILDURL(pkgName)
resp, err := httpClient.Get(packageURL)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, ErrABSPackageNotFound
}
defer resp.Body.Close()
pkgBuild, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return pkgBuild, nil
}
// ABSPKGBUILDRepo retrieves the PKGBUILD repository to a dest directory.
func ABSPKGBUILDRepo(ctx context.Context, cmdBuilder exe.GitCmdBuilder,
dbName, pkgName, dest string, force bool,
) (bool, error) {
pkgURL := getPackageRepoURL(pkgName)
return downloadGitRepo(ctx, cmdBuilder, pkgURL,
pkgName, dest, force, "--single-branch")
}

331
pkg/download/abs_test.go Normal file
View File

@ -0,0 +1,331 @@
//go:build !integration
// +build !integration
package download
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/Jguer/yay/v12/pkg/settings/exe"
)
const gitExtrasPKGBUILD = `pkgname=git-extras
pkgver=6.1.0
pkgrel=1
pkgdesc="GIT utilities -- repo summary, commit counting, repl, changelog population and more"
arch=('any')
url="https://github.com/tj/${pkgname}"
license=('MIT')
depends=('git')
source=("${pkgname}-${pkgver}.tar.gz::${url}/archive/${pkgver}.tar.gz")
sha256sums=('7be0b15ee803d76d2c2e8036f5d9db6677f2232bb8d2c4976691ff7ae026a22f')
b2sums=('3450edecb3116e19ffcf918b118aee04f025c06d812e29e8701f35a3c466b13d2578d41c8e1ee93327743d0019bf98bb3f397189e19435f89e3a259ff1b82747')
package() {
cd "${srcdir}/${pkgname}-${pkgver}"
# avoid annoying interactive prompts if an alias is in your gitconfig
export GIT_CONFIG=/dev/null
make DESTDIR="${pkgdir}" PREFIX=/usr SYSCONFDIR=/etc install
install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
}`
func Test_getPackageURL(t *testing.T) {
t.Parallel()
type args struct {
db string
pkgName string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "extra package",
args: args{
db: "extra",
pkgName: "kitty",
},
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/kitty/-/raw/main/PKGBUILD",
wantErr: false,
},
{
name: "core package",
args: args{
db: "core",
pkgName: "linux",
},
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/linux/-/raw/main/PKGBUILD",
wantErr: false,
},
{
name: "personal repo package",
args: args{
db: "sweswe",
pkgName: "zabix",
},
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/zabix/-/raw/main/PKGBUILD",
wantErr: false,
},
{
name: "special name +",
args: args{
db: "core",
pkgName: "my+package",
},
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/my-package/-/raw/main/PKGBUILD",
wantErr: false,
},
{
name: "special name %",
args: args{
db: "core",
pkgName: "my%package",
},
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/my-package/-/raw/main/PKGBUILD",
wantErr: false,
},
{
name: "special name _-",
args: args{
db: "core",
pkgName: "my_-package",
},
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/my-package/-/raw/main/PKGBUILD",
wantErr: false,
},
{
name: "special name ++",
args: args{
db: "core",
pkgName: "my++package",
},
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/mypluspluspackage/-/raw/main/PKGBUILD",
wantErr: false,
},
{
name: "special name tree",
args: args{
db: "sweswe",
pkgName: "tree",
},
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/unix-tree/-/raw/main/PKGBUILD",
wantErr: false,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := getPackagePKGBUILDURL(tt.args.pkgName)
assert.Equal(t, tt.want, got)
})
}
}
func TestGetABSPkgbuild(t *testing.T) {
t.Parallel()
type args struct {
dbName string
body string
status int
pkgName string
wantURL string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "found package",
args: args{
dbName: "core",
body: gitExtrasPKGBUILD,
status: 200,
pkgName: "git-extras",
wantURL: "https://gitlab.archlinux.org/archlinux/packaging/packages/git-extras/-/raw/main/PKGBUILD",
},
want: gitExtrasPKGBUILD,
wantErr: false,
},
{
name: "not found package",
args: args{
dbName: "core",
body: "",
status: 404,
pkgName: "git-git",
wantURL: "https://gitlab.archlinux.org/archlinux/packaging/packages/git-git/-/raw/main/PKGBUILD",
},
want: "",
wantErr: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
httpClient := &testClient{
t: t,
wantURL: tt.args.wantURL,
body: tt.args.body,
status: tt.args.status,
}
got, err := ABSPKGBUILD(httpClient, tt.args.dbName, tt.args.pkgName)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.want, string(got))
})
}
}
func Test_getPackageRepoURL(t *testing.T) {
t.Parallel()
type args struct {
pkgName string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "extra package",
args: args{pkgName: "zoxide"},
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/zoxide.git",
wantErr: false,
},
{
name: "core package",
args: args{pkgName: "linux"},
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/linux.git",
wantErr: false,
},
{
name: "personal repo package",
args: args{pkgName: "sweswe"},
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/sweswe.git",
wantErr: false,
},
{
name: "special name +",
args: args{pkgName: "my+package"},
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/my-package.git",
wantErr: false,
},
{
name: "special name %",
args: args{pkgName: "my%package"},
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/my-package.git",
wantErr: false,
},
{
name: "special name _-",
args: args{pkgName: "my_-package"},
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/my-package.git",
wantErr: false,
},
{
name: "special name ++",
args: args{pkgName: "my++package"},
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/mypluspluspackage.git",
wantErr: false,
},
{
name: "special name tree",
args: args{pkgName: "tree"},
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/unix-tree.git",
wantErr: false,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := getPackageRepoURL(tt.args.pkgName)
assert.Equal(t, tt.want, got)
})
}
}
// GIVEN no previous existing folder
// WHEN ABSPKGBUILDRepo is called
// THEN a clone command should be formed
func TestABSPKGBUILDRepo(t *testing.T) {
t.Parallel()
cmdRunner := &testRunner{}
want := "/usr/local/bin/git --no-replace-objects -C /tmp/doesnt-exist clone --no-progress --single-branch https://gitlab.archlinux.org/archlinux/packaging/packages/linux.git linux"
if os.Getuid() == 0 {
ld := "systemd-run"
if path, _ := exec.LookPath(ld); path != "" {
ld = path
}
want = fmt.Sprintf("%s --service-type=oneshot --pipe --wait --pty --quiet -p DynamicUser=yes -p CacheDirectory=yay -E HOME=/tmp --no-replace-objects -C /tmp/doesnt-exist clone --no-progress --single-branch https://gitlab.archlinux.org/archlinux/packaging/packages/linux.git linux", ld)
}
cmdBuilder := &testGitBuilder{
index: 0,
test: t,
want: want,
parentBuilder: &exe.CmdBuilder{
Runner: cmdRunner,
GitBin: "/usr/local/bin/git",
GitFlags: []string{"--no-replace-objects"},
},
}
newClone, err := ABSPKGBUILDRepo(context.Background(), cmdBuilder, "core", "linux", "/tmp/doesnt-exist", false)
assert.NoError(t, err)
assert.Equal(t, true, newClone)
}
// GIVEN a previous existing folder with permissions
// WHEN ABSPKGBUILDRepo is called
// THEN a pull command should be formed
func TestABSPKGBUILDRepoExistsPerms(t *testing.T) {
t.Parallel()
dir := t.TempDir()
os.MkdirAll(filepath.Join(dir, "linux", ".git"), 0o777)
want := fmt.Sprintf("/usr/local/bin/git --no-replace-objects -C %s/linux pull --rebase --autostash", dir)
if os.Getuid() == 0 {
ld := "systemd-run"
if path, _ := exec.LookPath(ld); path != "" {
ld = path
}
want = fmt.Sprintf("%s --service-type=oneshot --pipe --wait --pty --quiet -p DynamicUser=yes -p CacheDirectory=yay -E HOME=/tmp --no-replace-objects -C %s/linux pull --rebase --autostash", ld, dir)
}
cmdRunner := &testRunner{}
cmdBuilder := &testGitBuilder{
index: 0,
test: t,
want: want,
parentBuilder: &exe.CmdBuilder{
Runner: cmdRunner,
GitBin: "/usr/local/bin/git",
GitFlags: []string{"--no-replace-objects"},
},
}
newClone, err := ABSPKGBUILDRepo(context.Background(), cmdBuilder, "core", "linux", dir, false)
assert.NoError(t, err)
assert.Equal(t, false, newClone)
}

100
pkg/download/aur.go Normal file
View File

@ -0,0 +1,100 @@
package download
import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"sync"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/multierror"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/text"
)
func AURPKGBUILD(httpClient httpRequestDoer, pkgName, aurURL string) ([]byte, error) {
values := url.Values{}
values.Set("h", pkgName)
pkgURL := aurURL + "/cgit/aur.git/plain/PKGBUILD?" + values.Encode()
resp, err := httpClient.Get(pkgURL)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, ErrAURPackageNotFound{pkgName: pkgName}
}
defer resp.Body.Close()
pkgBuild, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return pkgBuild, nil
}
// AURPkgbuildRepo retrieves the PKGBUILD repository to a dest directory.
func AURPKGBUILDRepo(ctx context.Context, cmdBuilder exe.GitCmdBuilder, aurURL, pkgName, dest string, force bool) (bool, error) {
pkgURL := fmt.Sprintf("%s/%s.git", aurURL, pkgName)
return downloadGitRepo(ctx, cmdBuilder, pkgURL, pkgName, dest, force)
}
func AURPKGBUILDRepos(
ctx context.Context,
cmdBuilder exe.GitCmdBuilder, logger *text.Logger,
targets []string, aurURL, dest string, force bool,
) (map[string]bool, error) {
cloned := make(map[string]bool, len(targets))
var (
mux sync.Mutex
errs multierror.MultiError
wg sync.WaitGroup
)
sem := make(chan uint8, MaxConcurrentFetch)
for _, target := range targets {
sem <- 1
wg.Add(1)
go func(target string) {
defer func() {
<-sem
wg.Done()
}()
newClone, err := AURPKGBUILDRepo(ctx, cmdBuilder, aurURL, target, dest, force)
mux.Lock()
progress := len(cloned)
if err != nil {
errs.Add(err)
mux.Unlock()
logger.OperationInfoln(
gotext.Get("(%d/%d) Failed to download PKGBUILD: %s",
progress, len(targets), text.Cyan(target)))
return
}
cloned[target] = newClone
progress = len(cloned)
mux.Unlock()
logger.OperationInfoln(
gotext.Get("(%d/%d) Downloaded PKGBUILD: %s",
progress, len(targets), text.Cyan(target)))
}(target)
}
wg.Wait()
return cloned, errs.Return()
}

165
pkg/download/aur_test.go Normal file
View File

@ -0,0 +1,165 @@
//go:build !integration
// +build !integration
package download
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/Jguer/yay/v12/pkg/settings/exe"
)
func TestGetAURPkgbuild(t *testing.T) {
t.Parallel()
type args struct {
body string
status int
pkgName string
wantURL string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "found package",
args: args{
body: gitExtrasPKGBUILD,
status: 200,
pkgName: "git-extras",
wantURL: "https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=git-extras",
},
want: gitExtrasPKGBUILD,
wantErr: false,
},
{
name: "not found package",
args: args{
body: "",
status: 404,
pkgName: "git-git",
wantURL: "https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=git-git",
},
want: "",
wantErr: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
httpClient := &testClient{
t: t,
wantURL: tt.args.wantURL,
body: tt.args.body,
status: tt.args.status,
}
got, err := AURPKGBUILD(httpClient, tt.args.pkgName, "https://aur.archlinux.org")
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.want, string(got))
})
}
}
// GIVEN no previous existing folder
// WHEN AURPKGBUILDRepo is called
// THEN a clone command should be formed
func TestAURPKGBUILDRepo(t *testing.T) {
t.Parallel()
want := "/usr/local/bin/git --no-replace-objects -C /tmp/doesnt-exist clone --no-progress https://aur.archlinux.org/yay-bin.git yay-bin"
if os.Getuid() == 0 {
ld := "systemd-run"
if path, _ := exec.LookPath(ld); path != "" {
ld = path
}
want = fmt.Sprintf("%s --service-type=oneshot --pipe --wait --pty --quiet -p DynamicUser=yes -p CacheDirectory=yay -E HOME=/tmp --no-replace-objects -C /tmp/doesnt-exist clone --no-progress https://aur.archlinux.org/yay-bin.git yay-bin", ld)
}
cmdRunner := &testRunner{}
cmdBuilder := &testGitBuilder{
index: 0,
test: t,
want: want,
parentBuilder: &exe.CmdBuilder{
Runner: cmdRunner,
GitBin: "/usr/local/bin/git",
GitFlags: []string{"--no-replace-objects"},
},
}
newCloned, err := AURPKGBUILDRepo(context.Background(), cmdBuilder, "https://aur.archlinux.org", "yay-bin", "/tmp/doesnt-exist", false)
assert.NoError(t, err)
assert.Equal(t, true, newCloned)
}
// GIVEN a previous existing folder with permissions
// WHEN AURPKGBUILDRepo is called
// THEN a pull command should be formed
func TestAURPKGBUILDRepoExistsPerms(t *testing.T) {
t.Parallel()
dir := t.TempDir()
os.MkdirAll(filepath.Join(dir, "yay-bin", ".git"), 0o777)
want := fmt.Sprintf("/usr/local/bin/git --no-replace-objects -C %s/yay-bin pull --rebase --autostash", dir)
if os.Getuid() == 0 {
ld := "systemd-run"
if path, _ := exec.LookPath(ld); path != "" {
ld = path
}
want = fmt.Sprintf("%s --service-type=oneshot --pipe --wait --pty --quiet -p DynamicUser=yes -p CacheDirectory=yay -E HOME=/tmp --no-replace-objects -C %s/yay-bin pull --rebase --autostash", ld, dir)
}
cmdRunner := &testRunner{}
cmdBuilder := &testGitBuilder{
index: 0,
test: t,
want: want,
parentBuilder: &exe.CmdBuilder{
Runner: cmdRunner,
GitBin: "/usr/local/bin/git",
GitFlags: []string{"--no-replace-objects"},
},
}
cloned, err := AURPKGBUILDRepo(context.Background(), cmdBuilder, "https://aur.archlinux.org", "yay-bin", dir, false)
assert.NoError(t, err)
assert.Equal(t, false, cloned)
}
func TestAURPKGBUILDRepos(t *testing.T) {
t.Parallel()
dir := t.TempDir()
os.MkdirAll(filepath.Join(dir, "yay-bin", ".git"), 0o777)
targets := []string{"yay", "yay-bin", "yay-git"}
cmdRunner := &testRunner{}
cmdBuilder := &testGitBuilder{
index: 0,
test: t,
want: "",
parentBuilder: &exe.CmdBuilder{
Runner: cmdRunner,
GitBin: "/usr/local/bin/git",
GitFlags: []string{},
},
}
cloned, err := AURPKGBUILDRepos(context.Background(), cmdBuilder, newTestLogger(), targets, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err)
assert.EqualValues(t, map[string]bool{"yay": true, "yay-bin": false, "yay-git": true}, cloned)
}

31
pkg/download/errors.go Normal file
View File

@ -0,0 +1,31 @@
package download
import (
"fmt"
"github.com/leonelquinteros/gotext"
)
// ErrAURPackageNotFound means that package was not found in AUR.
type ErrAURPackageNotFound struct {
pkgName string
}
func (e ErrAURPackageNotFound) Error() string {
return fmt.Sprintln(gotext.Get("package not found in AUR"), ":", e.pkgName)
}
type ErrGetPKGBUILDRepo struct {
inner error
pkgName string
errOut string
}
func (e ErrGetPKGBUILDRepo) Error() string {
return fmt.Sprintln(gotext.Get("error fetching %s: %s", e.pkgName, e.errOut),
"\n\t context:", e.inner.Error())
}
func (e *ErrGetPKGBUILDRepo) Unwrap() error {
return e.inner
}

251
pkg/download/unified.go Normal file
View File

@ -0,0 +1,251 @@
package download
import (
"context"
"net/http"
"os"
"path/filepath"
"sync"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/aur"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/multierror"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
)
type httpRequestDoer interface {
Get(string) (*http.Response, error)
}
type DBSearcher interface {
SyncPackage(string) db.IPackage
SyncPackageFromDB(string, string) db.IPackage
}
func downloadGitRepo(ctx context.Context, cmdBuilder exe.GitCmdBuilder,
pkgURL, pkgName, dest string, force bool, gitArgs ...string,
) (bool, error) {
finalDir := filepath.Join(dest, pkgName)
newClone := true
switch _, err := os.Stat(filepath.Join(finalDir, ".git")); {
case os.IsNotExist(err) || (err == nil && force):
if _, errD := os.Stat(finalDir); force && errD == nil {
if errR := os.RemoveAll(finalDir); errR != nil {
return false, ErrGetPKGBUILDRepo{inner: errR, pkgName: pkgName, errOut: ""}
}
}
gitArgs = append(gitArgs, pkgURL, pkgName)
cloneArgs := make([]string, 0, len(gitArgs)+4)
cloneArgs = append(cloneArgs, "clone", "--no-progress")
cloneArgs = append(cloneArgs, gitArgs...)
cmd := cmdBuilder.BuildGitCmd(ctx, dest, cloneArgs...)
_, stderr, errCapture := cmdBuilder.Capture(cmd)
if errCapture != nil {
return false, ErrGetPKGBUILDRepo{inner: errCapture, pkgName: pkgName, errOut: stderr}
}
case err != nil:
return false, ErrGetPKGBUILDRepo{
inner: err,
pkgName: pkgName,
errOut: gotext.Get("error reading %s", filepath.Join(dest, pkgName, ".git")),
}
default:
cmd := cmdBuilder.BuildGitCmd(ctx, filepath.Join(dest, pkgName), "pull", "--rebase", "--autostash")
_, stderr, errCmd := cmdBuilder.Capture(cmd)
if errCmd != nil {
return false, ErrGetPKGBUILDRepo{inner: errCmd, pkgName: pkgName, errOut: stderr}
}
newClone = false
}
return newClone, nil
}
func getURLName(pkg db.IPackage) string {
name := pkg.Base()
if name == "" {
name = pkg.Name()
}
return name
}
func PKGBUILDs(dbExecutor DBSearcher, aurClient aur.QueryClient, httpClient *http.Client,
logger *text.Logger, targets []string, aurURL string, mode parser.TargetMode,
) (map[string][]byte, error) {
pkgbuilds := make(map[string][]byte, len(targets))
var (
mux sync.Mutex
errs multierror.MultiError
wg sync.WaitGroup
)
sem := make(chan uint8, MaxConcurrentFetch)
for _, target := range targets {
// Probably replaceable by something in query.
dbName, name, isAUR, toSkip := getPackageUsableName(dbExecutor, aurClient, logger, target, mode)
if toSkip {
continue
}
sem <- 1
wg.Add(1)
go func(target, dbName, pkgName string, aur bool) {
var (
err error
pkgbuild []byte
)
if aur {
pkgbuild, err = AURPKGBUILD(httpClient, pkgName, aurURL)
} else {
pkgbuild, err = ABSPKGBUILD(httpClient, dbName, pkgName)
}
if err == nil {
mux.Lock()
pkgbuilds[target] = pkgbuild
mux.Unlock()
} else {
errs.Add(err)
}
<-sem
wg.Done()
}(target, dbName, name, isAUR)
}
wg.Wait()
return pkgbuilds, errs.Return()
}
func PKGBUILDRepos(ctx context.Context, dbExecutor DBSearcher, aurClient aur.QueryClient,
cmdBuilder exe.GitCmdBuilder, logger *text.Logger,
targets []string, mode parser.TargetMode, aurURL, dest string, force bool,
) (map[string]bool, error) {
cloned := make(map[string]bool, len(targets))
var (
mux sync.Mutex
errs multierror.MultiError
wg sync.WaitGroup
)
sem := make(chan uint8, MaxConcurrentFetch)
for _, target := range targets {
// Probably replaceable by something in query.
dbName, name, isAUR, toSkip := getPackageUsableName(dbExecutor, aurClient, logger, target, mode)
if toSkip {
continue
}
sem <- 1
wg.Add(1)
go func(target, dbName, pkgName string, aur bool) {
var (
err error
newClone bool
)
if aur {
newClone, err = AURPKGBUILDRepo(ctx, cmdBuilder, aurURL, pkgName, dest, force)
} else {
newClone, err = ABSPKGBUILDRepo(ctx, cmdBuilder, dbName, pkgName, dest, force)
}
progress := 0
if err != nil {
errs.Add(err)
} else {
mux.Lock()
cloned[target] = newClone
progress = len(cloned)
mux.Unlock()
}
if aur {
logger.OperationInfoln(
gotext.Get("(%d/%d) Downloaded PKGBUILD: %s",
progress, len(targets), text.Cyan(pkgName)))
} else {
logger.OperationInfoln(
gotext.Get("(%d/%d) Downloaded PKGBUILD from ABS: %s",
progress, len(targets), text.Cyan(pkgName)))
}
<-sem
wg.Done()
}(target, dbName, name, isAUR)
}
wg.Wait()
return cloned, errs.Return()
}
// TODO: replace with dep.ResolveTargets.
func getPackageUsableName(dbExecutor DBSearcher, aurClient aur.QueryClient,
logger *text.Logger, target string, mode parser.TargetMode,
) (dbname, pkgname string, isAUR, toSkip bool) {
dbName, name := text.SplitDBFromName(target)
if dbName != "aur" && mode.AtLeastRepo() {
var pkg db.IPackage
if dbName != "" {
pkg = dbExecutor.SyncPackageFromDB(name, dbName)
} else {
pkg = dbExecutor.SyncPackage(name)
}
if pkg != nil {
name = getURLName(pkg)
dbName = pkg.DB().Name()
return dbName, name, false, false
}
// If the package is not found in the database and it was expected to be
if pkg == nil && dbName != "" {
return dbName, name, true, true
}
}
if mode == parser.ModeRepo {
return dbName, name, true, true
}
pkgs, err := aurClient.Get(context.Background(), &aur.Query{
By: aur.Name,
Contains: false,
Needles: []string{name},
})
if err != nil {
logger.Warnln(err)
return dbName, name, true, true
}
if len(pkgs) == 0 {
return dbName, name, true, true
}
return "aur", name, true, false
}

View File

@ -0,0 +1,106 @@
//go:build integration
// +build integration
package download
import (
"context"
"net/http"
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/Jguer/aur"
mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
)
func TestIntegrationPKGBUILDReposDefinedDBClone(t *testing.T) {
dir := t.TempDir()
mockClient := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{{}}, nil // fakes a package found for all
},
}
targets := []string{"core/linux", "yay-bin", "yay-git"}
testLogger := text.NewLogger(os.Stdout, os.Stderr, strings.NewReader(""), true, "test")
cmdRunner := &exe.OSRunner{Log: testLogger}
cmdBuilder := &exe.CmdBuilder{
Runner: cmdRunner,
GitBin: "git",
GitFlags: []string{},
Log: testLogger,
}
searcher := &testDBSearcher{
absPackagesDB: map[string]string{"linux": "core"},
}
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, testLogger.Child("test"),
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err)
assert.EqualValues(t, map[string]bool{"core/linux": true, "yay-bin": true, "yay-git": true}, cloned)
}
func TestIntegrationPKGBUILDReposNotExist(t *testing.T) {
dir := t.TempDir()
mockClient := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{{}}, nil // fakes a package found for all
},
}
targets := []string{"core/yay", "yay-bin", "yay-git"}
testLogger := text.NewLogger(os.Stdout, os.Stderr, strings.NewReader(""), true, "test")
cmdRunner := &exe.OSRunner{Log: testLogger}
cmdBuilder := &exe.CmdBuilder{
Runner: cmdRunner,
GitBin: "git",
GitFlags: []string{},
Log: testLogger,
}
searcher := &testDBSearcher{
absPackagesDB: map[string]string{"yay": "core"},
}
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, testLogger.Child("test"),
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
assert.Error(t, err)
assert.EqualValues(t, map[string]bool{"yay-bin": true, "yay-git": true}, cloned)
}
// GIVEN 2 aur packages and 1 in repo
// WHEN defining as specified targets
// THEN all aur be found and cloned
func TestIntegrationPKGBUILDFull(t *testing.T) {
mockClient := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{{}}, nil
},
}
testLogger := text.NewLogger(os.Stdout, os.Stderr, strings.NewReader(""), true, "test")
targets := []string{"core/linux", "aur/yay-bin", "yay-git"}
searcher := &testDBSearcher{
absPackagesDB: map[string]string{"linux": "core"},
}
fetched, err := PKGBUILDs(searcher, mockClient, &http.Client{}, testLogger.Child("test"),
targets, "https://aur.archlinux.org", parser.ModeAny)
assert.NoError(t, err)
for _, target := range targets {
assert.Contains(t, fetched, target)
assert.NotEmpty(t, fetched[target])
}
}

View File

@ -0,0 +1,281 @@
//go:build !integration
// +build !integration
package download
import (
"context"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/h2non/gock.v1"
"github.com/Jguer/aur"
mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
)
func newTestLogger() *text.Logger {
return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
}
// GIVEN 2 aur packages and 1 in repo
// GIVEN package in repo is already present
// WHEN defining package db as a target
// THEN all should be found and cloned, except the repo one
func TestPKGBUILDReposDefinedDBPull(t *testing.T) {
t.Parallel()
dir := t.TempDir()
mockClient := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{{}}, nil // fakes a package found for all
},
}
testLogger := text.NewLogger(os.Stdout, os.Stderr, strings.NewReader(""), true, "test")
os.MkdirAll(filepath.Join(dir, "yay", ".git"), 0o777)
targets := []string{"core/yay", "yay-bin", "yay-git"}
cmdRunner := &testRunner{}
cmdBuilder := &testGitBuilder{
index: 0,
test: t,
parentBuilder: &exe.CmdBuilder{
Runner: cmdRunner,
GitBin: "/usr/local/bin/git",
GitFlags: []string{},
Log: testLogger,
},
}
searcher := &testDBSearcher{
absPackagesDB: map[string]string{"yay": "core"},
}
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, newTestLogger(),
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err)
assert.EqualValues(t, map[string]bool{"core/yay": false, "yay-bin": true, "yay-git": true}, cloned)
}
// GIVEN 2 aur packages and 1 in repo
// WHEN defining package db as a target
// THEN all should be found and cloned
func TestPKGBUILDReposDefinedDBClone(t *testing.T) {
t.Parallel()
dir := t.TempDir()
mockClient := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{{}}, nil // fakes a package found for all
},
}
targets := []string{"core/yay", "yay-bin", "yay-git"}
cmdRunner := &testRunner{}
cmdBuilder := &testGitBuilder{
index: 0,
test: t,
parentBuilder: &exe.CmdBuilder{
Runner: cmdRunner,
GitBin: "/usr/local/bin/git",
GitFlags: []string{},
},
}
searcher := &testDBSearcher{
absPackagesDB: map[string]string{"yay": "core"},
}
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, newTestLogger(),
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err)
assert.EqualValues(t, map[string]bool{"core/yay": true, "yay-bin": true, "yay-git": true}, cloned)
}
// GIVEN 2 aur packages and 1 in repo
// WHEN defining as non specified targets
// THEN all should be found and cloned
func TestPKGBUILDReposClone(t *testing.T) {
t.Parallel()
dir := t.TempDir()
mockClient := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{{}}, nil // fakes a package found for all
},
}
targets := []string{"yay", "yay-bin", "yay-git"}
cmdRunner := &testRunner{}
cmdBuilder := &testGitBuilder{
index: 0,
test: t,
parentBuilder: &exe.CmdBuilder{
Runner: cmdRunner,
GitBin: "/usr/local/bin/git",
GitFlags: []string{},
},
}
searcher := &testDBSearcher{
absPackagesDB: map[string]string{"yay": "core"},
}
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, newTestLogger(),
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err)
assert.EqualValues(t, map[string]bool{"yay": true, "yay-bin": true, "yay-git": true}, cloned)
}
// GIVEN 2 aur packages and 1 in repo but wrong db
// WHEN defining as non specified targets
// THEN all aur be found and cloned
func TestPKGBUILDReposNotFound(t *testing.T) {
t.Parallel()
dir := t.TempDir()
mockClient := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{{}}, nil // fakes a package found for all
},
}
targets := []string{"extra/yay", "yay-bin", "yay-git"}
cmdRunner := &testRunner{}
cmdBuilder := &testGitBuilder{
index: 0,
test: t,
parentBuilder: &exe.CmdBuilder{
Runner: cmdRunner,
GitBin: "/usr/local/bin/git",
GitFlags: []string{},
},
}
searcher := &testDBSearcher{
absPackagesDB: map[string]string{"yay": "core"},
}
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, newTestLogger(),
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err)
assert.EqualValues(t, map[string]bool{"yay-bin": true, "yay-git": true}, cloned)
}
// GIVEN 2 aur packages and 1 in repo
// WHEN defining as non specified targets in repo mode
// THEN only repo should be cloned
func TestPKGBUILDReposRepoMode(t *testing.T) {
t.Parallel()
dir := t.TempDir()
mockClient := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil // fakes a package found for all
},
}
targets := []string{"yay", "yay-bin", "yay-git"}
cmdRunner := &testRunner{}
cmdBuilder := &testGitBuilder{
index: 0,
test: t,
parentBuilder: &exe.CmdBuilder{
Runner: cmdRunner,
GitBin: "/usr/local/bin/git",
GitFlags: []string{},
},
}
searcher := &testDBSearcher{
absPackagesDB: map[string]string{"yay": "core"},
}
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, newTestLogger(),
targets, parser.ModeRepo, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err)
assert.EqualValues(t, map[string]bool{"yay": true}, cloned)
}
// GIVEN 2 aur packages and 1 in repo
// WHEN defining as specified targets
// THEN all aur be found and cloned
func TestPKGBUILDFull(t *testing.T) {
t.Parallel()
mockClient := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{{}}, nil
},
}
gock.New("https://aur.archlinux.org").
Get("/cgit/aur.git/plain/PKGBUILD").MatchParam("h", "yay-git").
Reply(200).
BodyString("example_yay-git")
gock.New("https://aur.archlinux.org").
Get("/cgit/aur.git/plain/PKGBUILD").MatchParam("h", "yay-bin").
Reply(200).
BodyString("example_yay-bin")
gock.New("https://gitlab.archlinux.org/").
Get("archlinux/packaging/packages/yay/-/raw/main/PKGBUILD").
Reply(200).
BodyString("example_yay")
defer gock.Off()
targets := []string{"core/yay", "aur/yay-bin", "yay-git"}
searcher := &testDBSearcher{
absPackagesDB: map[string]string{"yay": "core"},
}
fetched, err := PKGBUILDs(searcher, mockClient, &http.Client{}, newTestLogger(),
targets, "https://aur.archlinux.org", parser.ModeAny)
assert.NoError(t, err)
assert.EqualValues(t, map[string][]byte{
"core/yay": []byte("example_yay"),
"aur/yay-bin": []byte("example_yay-bin"),
"yay-git": []byte("example_yay-git"),
}, fetched)
}
// GIVEN 2 aur packages and 1 in repo
// WHEN aur packages are not found
// only repo should be cloned
func TestPKGBUILDReposMissingAUR(t *testing.T) {
t.Parallel()
dir := t.TempDir()
mockClient := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil // fakes a package found for all
},
}
targets := []string{"core/yay", "aur/yay-bin", "aur/yay-git"}
cmdRunner := &testRunner{}
cmdBuilder := &testGitBuilder{
index: 0,
test: t,
parentBuilder: &exe.CmdBuilder{
Runner: cmdRunner,
GitBin: "/usr/local/bin/git",
GitFlags: []string{},
},
}
searcher := &testDBSearcher{
absPackagesDB: map[string]string{"yay": "core"},
}
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, newTestLogger(),
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err)
assert.EqualValues(t, map[string]bool{"core/yay": true}, cloned)
}

120
pkg/download/utils_test.go Normal file
View File

@ -0,0 +1,120 @@
package download
import (
"context"
"io"
"net/http"
"os/exec"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/Jguer/go-alpm/v2"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/settings/exe"
)
type testRunner struct{}
func (t *testRunner) Capture(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return "", "", nil
}
func (t *testRunner) Show(cmd *exec.Cmd) error {
return nil
}
type testGitBuilder struct {
index int
test *testing.T
want string
parentBuilder *exe.CmdBuilder
}
func (t *testGitBuilder) BuildGitCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd {
cmd := t.parentBuilder.BuildGitCmd(ctx, dir, extraArgs...)
if t.want != "" {
assert.Equal(t.test, t.want, cmd.String())
}
return cmd
}
func (c *testGitBuilder) Show(cmd *exec.Cmd) error {
return c.parentBuilder.Show(cmd)
}
func (c *testGitBuilder) Capture(cmd *exec.Cmd) (stdout, stderr string, err error) {
return c.parentBuilder.Capture(cmd)
}
type (
testDB struct {
alpm.IDB
name string
}
testPackage struct {
db.IPackage
name string
base string
db *testDB
}
testDBSearcher struct {
absPackagesDB map[string]string
}
testClient struct {
t *testing.T
wantURL string
body string
status int
}
)
func (d *testDB) Name() string {
return d.name
}
func (p *testPackage) Name() string {
return p.name
}
func (p *testPackage) Base() string {
return p.base
}
func (p *testPackage) DB() alpm.IDB {
return p.db
}
func (d *testDBSearcher) SyncPackage(name string) db.IPackage {
if v, ok := d.absPackagesDB[name]; ok {
return &testPackage{
name: name,
base: name,
db: &testDB{name: v},
}
}
return nil
}
func (d *testDBSearcher) SyncPackageFromDB(name string, db string) db.IPackage {
if v, ok := d.absPackagesDB[name]; ok && v == db {
return &testPackage{
name: name,
base: name,
db: &testDB{name: v},
}
}
return nil
}
func (t *testClient) Get(url string) (*http.Response, error) {
assert.Equal(t.t, t.wantURL, url)
return &http.Response{StatusCode: t.status, Body: io.NopCloser(strings.NewReader(t.body))}, nil
}

View File

@ -5,33 +5,33 @@ import (
"strings"
"unicode"
"github.com/Jguer/yay/v10/pkg/stringset"
mapset "github.com/deckarep/golang-set/v2"
)
// IntRange stores a max and min amount for range
// IntRange stores a max and min amount for range.
type IntRange struct {
min int
max int
}
// IntRanges is a slice of IntRange
// IntRanges is a slice of IntRange.
type IntRanges []IntRange
func makeIntRange(min, max int) IntRange {
func makeIntRange(minVal, maxVal int) IntRange {
return IntRange{
min,
max,
min: minVal,
max: maxVal,
}
}
// Get returns true if the argument n is included in the closed range
// between min and max
// between min and max.
func (r IntRange) Get(n int) bool {
return n >= r.min && n <= r.max
}
// Get returns true if the argument n is included in the closed range
// between min and max of any of the provided IntRanges
// between min and max of any of the provided IntRanges.
func (rs IntRanges) Get(n int) bool {
for _, r := range rs {
if r.Get(n) {
@ -42,22 +42,6 @@ func (rs IntRanges) Get(n int) bool {
return false
}
// Min returns min value between a and b
func Min(a, b int) int {
if a < b {
return a
}
return b
}
// Max returns max value between a and b
func Max(a, b int) int {
if a < b {
return b
}
return a
}
// ParseNumberMenu parses input for number menus split by spaces or commas
// supports individual selection: 1 2 3 4
// supports range selections: 1-4 10-20
@ -67,22 +51,26 @@ func Max(a, b int) int {
// respectively. other holds anything that can't be parsed as an int. This is
// intended to allow words inside of number menus. e.g. 'all' 'none' 'abort'
// of course the implementation is up to the caller, this function mearley parses
// the input and organizes it
// the input and organizes it.
func ParseNumberMenu(input string) (include, exclude IntRanges,
otherInclude, otherExclude stringset.StringSet) {
otherInclude, otherExclude mapset.Set[string],
) {
include = make(IntRanges, 0)
exclude = make(IntRanges, 0)
otherInclude = make(stringset.StringSet)
otherExclude = make(stringset.StringSet)
otherInclude = mapset.NewThreadUnsafeSet[string]()
otherExclude = mapset.NewThreadUnsafeSet[string]()
words := strings.FieldsFunc(input, func(c rune) bool {
return unicode.IsSpace(c) || c == ','
})
for _, word := range words {
var num1 int
var num2 int
var err error
var (
num1 int
num2 int
err error
)
invert := false
other := otherInclude
@ -96,22 +84,22 @@ func ParseNumberMenu(input string) (include, exclude IntRanges,
num1, err = strconv.Atoi(ranges[0])
if err != nil {
other.Set(strings.ToLower(word))
other.Add(strings.ToLower(word))
continue
}
if len(ranges) == 2 {
num2, err = strconv.Atoi(ranges[1])
if err != nil {
other.Set(strings.ToLower(word))
other.Add(strings.ToLower(word))
continue
}
} else {
num2 = num1
}
mi := Min(num1, num2)
ma := Max(num1, num2)
mi := min(num1, num2)
ma := max(num1, num2)
if !invert {
include = append(include, makeIntRange(mi, ma))

View File

@ -1,17 +1,22 @@
//go:build !integration
// +build !integration
package intrange
import (
"testing"
"github.com/Jguer/yay/v10/pkg/stringset"
mapset "github.com/deckarep/golang-set/v2"
"github.com/stretchr/testify/assert"
)
func TestParseNumberMenu(t *testing.T) {
t.Parallel()
type result struct {
Include IntRanges
Exclude IntRanges
OtherInclude stringset.StringSet
OtherExclude stringset.StringSet
OtherInclude mapset.Set[string]
OtherExclude mapset.Set[string]
}
inputs := []string{
@ -30,60 +35,73 @@ func TestParseNumberMenu(t *testing.T) {
}
expected := []result{
{IntRanges{makeIntRange(1, 1),
{IntRanges{
makeIntRange(1, 1),
makeIntRange(2, 2),
makeIntRange(3, 3),
makeIntRange(4, 4),
makeIntRange(5, 5)}, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
{IntRanges{makeIntRange(1, 10),
makeIntRange(5, 15)}, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
{IntRanges{makeIntRange(5, 10),
makeIntRange(85, 90)}, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
{IntRanges{makeIntRange(1, 1),
makeIntRange(5, 5),
}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()},
{IntRanges{
makeIntRange(1, 10),
makeIntRange(5, 15),
}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()},
{IntRanges{
makeIntRange(5, 10),
makeIntRange(85, 90),
}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()},
{
IntRanges{
makeIntRange(1, 1),
makeIntRange(99, 99),
makeIntRange(60, 62)},
IntRanges{makeIntRange(2, 2),
makeIntRange(60, 62),
},
IntRanges{
makeIntRange(2, 2),
makeIntRange(5, 10),
makeIntRange(38, 40),
makeIntRange(123, 123)},
make(stringset.StringSet), make(stringset.StringSet)},
{IntRanges{}, IntRanges{}, stringset.Make("abort", "all", "none"), make(stringset.StringSet)},
{IntRanges{}, IntRanges{}, stringset.Make("a-b"), stringset.Make("abort", "a-b")},
{IntRanges{}, IntRanges{}, stringset.Make("-9223372036854775809-9223372036854775809"), make(stringset.StringSet)},
{IntRanges{makeIntRange(1, 1),
makeIntRange(123, 123),
},
mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string](),
},
{IntRanges{}, IntRanges{}, mapset.NewThreadUnsafeSet("abort", "all", "none"), mapset.NewThreadUnsafeSet[string]()},
{IntRanges{}, IntRanges{}, mapset.NewThreadUnsafeSet("a-b"), mapset.NewThreadUnsafeSet("abort", "a-b")},
{IntRanges{}, IntRanges{}, mapset.NewThreadUnsafeSet("-9223372036854775809-9223372036854775809"), mapset.NewThreadUnsafeSet[string]()},
{IntRanges{
makeIntRange(1, 1),
makeIntRange(2, 2),
makeIntRange(3, 3),
makeIntRange(4, 4),
makeIntRange(5, 5)}, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
{IntRanges{makeIntRange(1, 1),
makeIntRange(5, 5),
}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()},
{IntRanges{
makeIntRange(1, 1),
makeIntRange(2, 2),
makeIntRange(3, 3),
makeIntRange(4, 4),
makeIntRange(5, 5),
makeIntRange(6, 6),
makeIntRange(7, 7),
makeIntRange(8, 8)}, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
{IntRanges{}, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
{IntRanges{}, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
{IntRanges{}, IntRanges{}, stringset.Make("a", "b", "c", "d", "e"), make(stringset.StringSet)},
makeIntRange(8, 8),
}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()},
{IntRanges{}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()},
{IntRanges{}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()},
{IntRanges{}, IntRanges{}, mapset.NewThreadUnsafeSet("a", "b", "c", "d", "e"), mapset.NewThreadUnsafeSet[string]()},
}
for n, in := range inputs {
res := expected[n]
include, exclude, otherInclude, otherExclude := ParseNumberMenu(in)
if !intRangesEqual(include, res.Include) ||
!intRangesEqual(exclude, res.Exclude) ||
!stringset.Equal(otherInclude, res.OtherInclude) ||
!stringset.Equal(otherExclude, res.OtherExclude) {
t.Fatalf("Test %d Failed: Expected: include=%+v exclude=%+v otherInclude=%+v otherExclude=%+v got include=%+v excluive=%+v otherInclude=%+v otherExclude=%+v",
n+1, res.Include, res.Exclude, res.OtherInclude, res.OtherExclude, include, exclude, otherInclude, otherExclude)
}
assert.True(t, intRangesEqual(include, res.Include), "Test %d Failed: Expected: include=%+v got include=%+v", n+1, res.Include, include)
assert.True(t, intRangesEqual(exclude, res.Exclude), "Test %d Failed: Expected: exclude=%+v got exclude=%+v", n+1, res.Exclude, exclude)
assert.True(t, otherInclude.Equal(res.OtherInclude), "Test %d Failed: Expected: otherInclude=%+v got otherInclude=%+v", n+1, res.OtherInclude, otherInclude)
assert.True(t, otherExclude.Equal(res.OtherExclude), "Test %d Failed: Expected: otherExclude=%+v got otherExclude=%+v", n+1, res.OtherExclude, otherExclude)
}
}
func TestIntRange_Get(t *testing.T) {
t.Parallel()
type fields struct {
min int
max int
@ -105,7 +123,9 @@ func TestIntRange_Get(t *testing.T) {
{name: "normal end range false", fields: fields{1, 2}, args: args{3}, want: false},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
r := IntRange{
min: tt.fields.min,
max: tt.fields.max,
@ -143,6 +163,7 @@ func intRangesEqual(a, b IntRanges) bool {
}
func TestIntRanges_Get(t *testing.T) {
t.Parallel()
type args struct {
n int
}
@ -162,7 +183,9 @@ func TestIntRanges_Get(t *testing.T) {
{name: "normal end range false", rs: IntRanges{{1, 2}}, args: args{3}, want: false},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
if got := tt.rs.Get(tt.args.n); got != tt.want {
t.Errorf("IntRanges.Get() = %v, want %v", got, tt.want)
}

78
pkg/menus/clean_menu.go Normal file
View File

@ -0,0 +1,78 @@
// Clean Build Menu functions
package menus
import (
"context"
"io"
"os"
mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/runtime"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/text"
)
func anyExistInCache(pkgbuildDirs map[string]string) bool {
for _, dir := range pkgbuildDirs {
if _, err := os.Stat(dir); !os.IsNotExist(err) {
return true
}
}
return false
}
func CleanFn(ctx context.Context, run *runtime.Runtime, w io.Writer,
pkgbuildDirsByBase map[string]string, installed mapset.Set[string],
) error {
if len(pkgbuildDirsByBase) == 0 {
return nil // no work to do
}
if !anyExistInCache(pkgbuildDirsByBase) {
return nil
}
skipFunc := func(pkg string) bool {
dir := pkgbuildDirsByBase[pkg]
// TOFIX: new install engine dir will always exist, check if unclean instead
if _, err := os.Stat(dir); os.IsNotExist(err) {
return true
}
return false
}
bases := make([]string, 0, len(pkgbuildDirsByBase))
for pkg := range pkgbuildDirsByBase {
bases = append(bases, pkg)
}
toClean, errClean := selectionMenu(run.Logger, pkgbuildDirsByBase, bases, installed,
gotext.Get("Packages to cleanBuild?"),
settings.NoConfirm, run.Cfg.AnswerClean, skipFunc)
if errClean != nil {
return errClean
}
for i, base := range toClean {
dir := pkgbuildDirsByBase[base]
run.Logger.OperationInfoln(gotext.Get("Deleting (%d/%d): %s", i+1, len(toClean), text.Cyan(dir)))
if err := run.CmdBuilder.Show(run.CmdBuilder.BuildGitCmd(ctx, dir, "reset", "--hard", "origin/HEAD")); err != nil {
run.Logger.Warnln(gotext.Get("Unable to clean:"), dir)
return err
}
if err := run.CmdBuilder.Show(run.CmdBuilder.BuildGitCmd(ctx, dir, "clean", "-fdx")); err != nil {
run.Logger.Warnln(gotext.Get("Unable to clean:"), dir)
return err
}
}
return nil
}

181
pkg/menus/diff_menu.go Normal file
View File

@ -0,0 +1,181 @@
// file dedicated to diff menu
package menus
import (
"context"
"fmt"
"io"
"strings"
mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/multierror"
"github.com/Jguer/yay/v12/pkg/runtime"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/text"
)
const (
gitEmptyTree = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
gitDiffRefName = "AUR_SEEN"
)
func showPkgbuildDiffs(ctx context.Context, cmdBuilder exe.ICmdBuilder, logger *text.Logger,
pkgbuildDirs map[string]string, bases []string,
) error {
var errMulti multierror.MultiError
for _, pkg := range bases {
dir := pkgbuildDirs[pkg]
start, err := getLastSeenHash(ctx, cmdBuilder, dir)
if err != nil {
errMulti.Add(err)
continue
}
if start != gitEmptyTree {
hasDiff, err := gitHasDiff(ctx, cmdBuilder, dir)
if err != nil {
errMulti.Add(err)
continue
}
if !hasDiff {
logger.Warnln(gotext.Get("%s: No changes -- skipping", text.Cyan(pkg)))
continue
}
}
args := []string{
"diff",
start + "..HEAD@{upstream}", "--src-prefix",
dir + "/", "--dst-prefix", dir + "/", "--", ".", ":(exclude).SRCINFO",
}
if text.UseColor {
args = append(args, "--color=always")
} else {
args = append(args, "--color=never")
}
_ = cmdBuilder.Show(cmdBuilder.BuildGitCmd(ctx, dir, args...))
}
return errMulti.Return()
}
// Check whether or not a diff exists between the last reviewed diff and
// HEAD@{upstream}.
func gitHasDiff(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) (bool, error) {
if gitHasLastSeenRef(ctx, cmdBuilder, dir) {
stdout, stderr, err := cmdBuilder.Capture(
cmdBuilder.BuildGitCmd(ctx, dir, "rev-parse", gitDiffRefName, "HEAD@{upstream}"))
if err != nil {
return false, fmt.Errorf("%s%w", stderr, err)
}
lines := strings.Split(stdout, "\n")
lastseen := lines[0]
upstream := lines[1]
return lastseen != upstream, nil
}
// If AUR_SEEN does not exists, we have never reviewed a diff for this package
// and should display it.
return true, nil
}
// Return whether or not we have reviewed a diff yet. It checks for the existence of
// AUR_SEEN in the git ref-list.
func gitHasLastSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) bool {
_, _, err := cmdBuilder.Capture(
cmdBuilder.BuildGitCmd(ctx,
dir, "rev-parse", "--quiet", "--verify", gitDiffRefName))
return err == nil
}
// Returns the last reviewed hash. If AUR_SEEN exists it will return this hash.
// If it does not it will return empty tree as no diff have been reviewed yet.
func getLastSeenHash(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) (string, error) {
if gitHasLastSeenRef(ctx, cmdBuilder, dir) {
stdout, stderr, err := cmdBuilder.Capture(
cmdBuilder.BuildGitCmd(ctx,
dir, "rev-parse", gitDiffRefName))
if err != nil {
return "", fmt.Errorf("%s %w", stderr, err)
}
lines := strings.Split(stdout, "\n")
return lines[0], nil
}
return gitEmptyTree, nil
}
// Update the AUR_SEEN ref to HEAD. We use this ref to determine which diff were
// reviewed by the user.
func gitUpdateSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) error {
_, stderr, err := cmdBuilder.Capture(
cmdBuilder.BuildGitCmd(ctx,
dir, "update-ref", gitDiffRefName, "HEAD"))
if err != nil {
return fmt.Errorf("%s %w", stderr, err)
}
return nil
}
func updatePkgbuildSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, pkgbuildDirs map[string]string, bases []string) error {
var errMulti multierror.MultiError
for _, pkg := range bases {
dir := pkgbuildDirs[pkg]
if err := gitUpdateSeenRef(ctx, cmdBuilder, dir); err != nil {
errMulti.Add(err)
}
}
return errMulti.Return()
}
func DiffFn(ctx context.Context, run *runtime.Runtime, w io.Writer,
pkgbuildDirsByBase map[string]string, installed mapset.Set[string],
) error {
if len(pkgbuildDirsByBase) == 0 {
return nil // no work to do
}
bases := make([]string, 0, len(pkgbuildDirsByBase))
for base := range pkgbuildDirsByBase {
bases = append(bases, base)
}
toDiff, errMenu := selectionMenu(run.Logger, pkgbuildDirsByBase, bases, installed, gotext.Get("Diffs to show?"),
settings.NoConfirm, run.Cfg.AnswerDiff, nil)
if errMenu != nil || len(toDiff) == 0 {
return errMenu
}
if errD := showPkgbuildDiffs(ctx, run.CmdBuilder, run.Logger, pkgbuildDirsByBase, toDiff); errD != nil {
return errD
}
run.Logger.Println()
if !run.Logger.ContinueTask(gotext.Get("Proceed with install?"), true, false) {
return settings.ErrUserAbort{}
}
if errUpd := updatePkgbuildSeenRef(ctx, run.CmdBuilder, pkgbuildDirsByBase, toDiff); errUpd != nil {
return errUpd
}
return nil
}

148
pkg/menus/edit_menu.go Normal file
View File

@ -0,0 +1,148 @@
// edit menu
package menus
import (
"context"
"errors"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
gosrc "github.com/Morganamilo/go-srcinfo"
mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/runtime"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/text"
)
// Editor returns the preferred system editor.
func editor(log *text.Logger, editorConfig, editorFlags string, noConfirm bool) (editor string, args []string) {
switch {
case editorConfig != "":
editor, err := exec.LookPath(editorConfig)
if err != nil {
log.Errorln(err)
} else {
return editor, strings.Fields(editorFlags)
}
fallthrough
case os.Getenv("VISUAL") != "":
if editorArgs := strings.Fields(os.Getenv("VISUAL")); len(editorArgs) != 0 {
editor, err := exec.LookPath(editorArgs[0])
if err != nil {
log.Errorln(err)
} else {
return editor, editorArgs[1:]
}
}
fallthrough
case os.Getenv("EDITOR") != "":
if editorArgs := strings.Fields(os.Getenv("EDITOR")); len(editorArgs) != 0 {
editor, err := exec.LookPath(editorArgs[0])
if err != nil {
log.Errorln(err)
} else {
return editor, editorArgs[1:]
}
}
fallthrough
default:
log.Errorln("\n", gotext.Get("%s is not set", text.Bold(text.Cyan("$EDITOR"))))
log.Warnln(gotext.Get("Add %s or %s to your environment variables", text.Bold(text.Cyan("$EDITOR")), text.Bold(text.Cyan("$VISUAL"))))
for {
log.Infoln(gotext.Get("Edit PKGBUILD with?"))
editorInput, err := log.GetInput("", noConfirm)
if err != nil {
log.Errorln(err)
continue
}
editorArgs := strings.Fields(editorInput)
if len(editorArgs) == 0 {
continue
}
editor, err := exec.LookPath(editorArgs[0])
if err != nil {
log.Errorln(err)
continue
}
return editor, editorArgs[1:]
}
}
}
func editPkgbuilds(log *text.Logger, pkgbuildDirs map[string]string, bases []string, editorConfig,
editorFlags string, srcinfos map[string]*gosrc.Srcinfo, noConfirm bool,
) error {
pkgbuilds := make([]string, 0, len(bases))
for _, pkg := range bases {
dir := pkgbuildDirs[pkg]
pkgbuilds = append(pkgbuilds, filepath.Join(dir, "PKGBUILD"))
if srcinfos != nil {
for _, splitPkg := range srcinfos[pkg].SplitPackages() {
if splitPkg.Install != "" {
pkgbuilds = append(pkgbuilds, filepath.Join(dir, splitPkg.Install))
}
}
}
}
if len(pkgbuilds) > 0 {
editor, editorArgs := editor(log, editorConfig, editorFlags, noConfirm)
editorArgs = append(editorArgs, pkgbuilds...)
editcmd := exec.Command(editor, editorArgs...)
editcmd.Stdin, editcmd.Stdout, editcmd.Stderr = os.Stdin, os.Stdout, os.Stderr
if err := editcmd.Run(); err != nil {
return errors.New(gotext.Get("editor did not exit successfully, aborting: %s", err))
}
}
return nil
}
func EditFn(ctx context.Context, run *runtime.Runtime, w io.Writer,
pkgbuildDirsByBase map[string]string, installed mapset.Set[string],
) error {
if len(pkgbuildDirsByBase) == 0 {
return nil // no work to do
}
bases := make([]string, 0, len(pkgbuildDirsByBase))
for pkg := range pkgbuildDirsByBase {
bases = append(bases, pkg)
}
toEdit, errMenu := selectionMenu(run.Logger, pkgbuildDirsByBase, bases, installed,
gotext.Get("PKGBUILDs to edit?"), settings.NoConfirm, run.Cfg.AnswerEdit, nil)
if errMenu != nil || len(toEdit) == 0 {
return errMenu
}
// TOFIX: remove or use srcinfo data
if errEdit := editPkgbuilds(run.Logger, pkgbuildDirsByBase,
toEdit, run.Cfg.Editor, run.Cfg.EditorFlags, nil, settings.NoConfirm); errEdit != nil {
return errEdit
}
run.Logger.Println()
if !run.Logger.ContinueTask(gotext.Get("Proceed with install?"), true, false) {
return settings.ErrUserAbort{}
}
return nil
}

103
pkg/menus/menu.go Normal file
View File

@ -0,0 +1,103 @@
package menus
import (
"fmt"
"os"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/intrange"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/text"
mapset "github.com/deckarep/golang-set/v2"
)
func pkgbuildNumberMenu(logger *text.Logger, pkgbuildDirs map[string]string,
bases []string, installed mapset.Set[string],
) {
toPrint := ""
for n, pkgBase := range bases {
dir := pkgbuildDirs[pkgBase]
toPrint += fmt.Sprintf(text.Magenta("%3d")+" %-40s", len(pkgbuildDirs)-n,
text.Bold(pkgBase))
if installed.Contains(pkgBase) {
toPrint += text.Bold(text.Green(gotext.Get(" (Installed)")))
}
// TODO: remove or refactor to check if git dir is unclean
if _, err := os.Stat(dir); !os.IsNotExist(err) {
toPrint += text.Bold(text.Green(gotext.Get(" (Build Files Exist)")))
}
toPrint += "\n"
}
logger.Print(toPrint)
}
func selectionMenu(logger *text.Logger, pkgbuildDirs map[string]string, bases []string, installed mapset.Set[string],
message string, noConfirm bool, defaultAnswer string, skipFunc func(string) bool,
) ([]string, error) {
selected := make([]string, 0)
pkgbuildNumberMenu(logger, pkgbuildDirs, bases, installed)
logger.Infoln(message)
logger.Infoln(gotext.Get("%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)", text.Cyan(gotext.Get("[N]one"))))
selectInput, err := logger.GetInput(defaultAnswer, noConfirm)
if err != nil {
return nil, err
}
eInclude, eExclude, eOtherInclude, eOtherExclude := intrange.ParseNumberMenu(selectInput)
eIsInclude := len(eExclude) == 0 && eOtherExclude.Cardinality() == 0
if eOtherInclude.Contains("abort") || eOtherInclude.Contains("ab") {
return nil, settings.ErrUserAbort{}
}
if eOtherInclude.Contains("n") || eOtherInclude.Contains("none") {
return selected, nil
}
for i, pkgBase := range bases {
if skipFunc != nil && skipFunc(pkgBase) {
continue
}
anyInstalled := installed.Contains(pkgBase)
if !eIsInclude && eExclude.Get(len(bases)-i) {
continue
}
if anyInstalled && (eOtherInclude.Contains("i") || eOtherInclude.Contains("installed")) {
selected = append(selected, pkgBase)
continue
}
if !anyInstalled && (eOtherInclude.Contains("no") || eOtherInclude.Contains("notinstalled")) {
selected = append(selected, pkgBase)
continue
}
if eOtherInclude.Contains("a") || eOtherInclude.Contains("all") {
selected = append(selected, pkgBase)
continue
}
if eIsInclude && (eInclude.Get(len(bases)-i) || eOtherInclude.Contains(pkgBase)) {
selected = append(selected, pkgBase)
}
if !eIsInclude && (!eExclude.Get(len(bases)-i) && !eOtherExclude.Contains(pkgBase)) {
selected = append(selected, pkgBase)
}
}
return selected, nil
}

View File

@ -2,13 +2,13 @@ package multierror
import "sync"
// MultiError type handles error accumulation from goroutines
// MultiError type handles error accumulation from goroutines.
type MultiError struct {
Errors []error
mux sync.Mutex
}
// Error turns the MultiError structure into a string
// Error turns the MultiError structure into a string.
func (err *MultiError) Error() string {
str := ""
@ -19,7 +19,7 @@ func (err *MultiError) Error() string {
return str[:len(str)-1]
}
// Add adds an error to the Multierror structure
// Add adds an error to the Multierror structure.
func (err *MultiError) Add(e error) {
if e == nil {
return
@ -31,7 +31,7 @@ func (err *MultiError) Add(e error) {
}
// Return is used as a wrapper on return on whether to return the
// MultiError Structure if errors exist or nil instead of delivering an empty structure
// MultiError Structure if errors exist or nil instead of delivering an empty structure.
func (err *MultiError) Return() error {
if len(err.Errors) > 0 {
return err

View File

@ -0,0 +1,11 @@
2019-12-20 Xorg cleanup requires manual intervention
2020-01-04 Now using Zstandard instead of xz for package compression
2020-01-15 rsync compatibility
2020-02-17 sshd needs restarting after upgrading to openssh-8.2p1
2020-02-22 Planet Arch Linux migration
2020-02-24 The Future of the Arch Linux Project Leader
2020-03-01 firewalld>=0.8.1-2 update requires manual intervention
2020-03-19 hplip 3.20.3-2 update requires manual intervention
2020-04-13 nss>=3.51.1-1 and lib32-nss>=3.51.1-1 updates require manual intervention
2020-04-14 zn_poly 0.9.2-2 update requires manual intervention

View File

@ -0,0 +1,114 @@
2019-12-20 Xorg cleanup requires manual intervention
In the process of Xorg cleanup the update requires manual
intervention when you hit this message:
:: installing xorgproto (2019.2-2) breaks dependency 'inputproto' required by lib32-libxi
:: installing xorgproto (2019.2-2) breaks dependency 'dmxproto' required by libdmx
:: installing xorgproto (2019.2-2) breaks dependency 'xf86dgaproto' required by libxxf86dga
:: installing xorgproto (2019.2-2) breaks dependency 'xf86miscproto' required by libxxf86misc

when updating, use: pacman -Rdd libdmx libxxf86dga libxxf86misc && pacman -Syu to perform the upgrade.

2020-01-04 Now using Zstandard instead of xz for package compression
As announced on the mailing list, on Friday, Dec 27 2019, our package compression scheme has changed from xz (.pkg.tar.xz) to zstd (.pkg.tar.zst).
zstd and xz trade blows in their compression ratio. Recompressing all packages to zstd with our options yields a total ~0.8% increase in package size on all of our packages combined, but the decompression time for all packages saw a ~1300% speedup.
We already have more than 545 zstd-compressed packages in our repositories, and as packages get updated more will keep rolling in. We have not found any user-facing issues as of yet, so things appear to be working.
As a packager, you will automatically start building .pkg.tar.zst packages if you are using the latest version of devtools (>= 20191227).
As an end-user no manual intervention is required, assuming that you have read and followed the news post from late last year.
If you nevertheless haven't updated libarchive since 2018, all hope is not lost! Binary builds of pacman-static are available from Eli Schwartz' personal repository (or direct link to binary), signed with their Trusted User keys, with which you can perform the update.

2020-01-15 rsync compatibility
Our rsync package was shipped with bundled zlib to provide compatibility
with the old-style --compress option up to version 3.1.0. Version 3.1.1 was
released on 2014-06-22 and is shipped by all major distributions now.
So we decided to finally drop the bundled library and ship a package with
system zlib. This also fixes security issues, actual ones and in future. Go
and blame those running old versions if you encounter errors with rsync
3.1.3-3.

2020-02-17 sshd needs restarting after upgrading to openssh-8.2p1
After upgrading to openssh-8.2p1, the existing SSH daemon will be unable to accept new connections. (See FS#65517.) When upgrading remote hosts, please make sure to restart the SSH daemon using systemctl restart sshd right after running pacman -Syu. If you are upgrading to openssh-8.2p1-3 or higher, this restart will happen automatically.

2020-02-22 Planet Arch Linux migration
The software behind planet.archlinux.org was implemented in Python 2 and is no longer maintained upstream. This functionality has now been implemented in archlinux.org's archweb backend which is actively maintained but offers a slightly different experience.
The most notable changes are the offered feeds and the feed location. Archweb only offers an Atom feed which is located at here.

2020-02-24 The Future of the Arch Linux Project Leader
Hello everyone,
Some of you may know me from the days when I was much more involved in Arch, but most of you probably just know me as a name on the website. Ive been with Arch for some time, taking the leadership of this beast over from Judd back in 2007. But, as these things often go, my involvement has slid down to minimal levels over time. Its high time that changes.
Arch Linux needs involved leadership to make hard decisions and direct the project where it needs to go. And I am not in a position to do this.
In a team effort, the Arch Linux staff devised a new process for determining future leaders. From now on, leaders will be elected by the staff for a term length of two years. Details of this new process can be found here
In the first official vote with Levente Polyak (anthraxx), Gaetan Bisson (vesath), Giancarlo Razzolini (grazzolini), and Sven-Hendrik Haase (svenstaro) as candidates, and through 58 verified votes, a winner was chosen:
Levente Polyak (anthraxx) will be taking over the reins of this ship. Congratulations!
Thanks for everything over all these years,
Aaron Griffin (phrakture)

2020-03-01 firewalld>=0.8.1-2 update requires manual intervention
The firewalld package prior to version 0.8.1-2 was missing the compiled python modules. This has been fixed in 0.8.1-2, so the upgrade will need to overwrite the untracked pyc files created. If you get errors like these
firewalld: /usr/lib/python3.8/site-packages/firewall/__pycache__/__init__.cpython-38.pyc exists in filesystem
firewalld: /usr/lib/python3.8/site-packages/firewall/__pycache__/client.cpython-38.pyc exists in filesystem
firewalld: /usr/lib/python3.8/site-packages/firewall/__pycache__/dbus_utils.cpython-38.pyc exists in filesystem
...many more...

when updating, use
pacman -Suy --overwrite /usr/lib/python3.8/site-packages/firewall/\*

to perform the upgrade.

2020-03-19 hplip 3.20.3-2 update requires manual intervention
The hplip package prior to version 3.20.3-2 was missing the compiled
python modules. This has been fixed in 3.20.3-2, so the upgrade will
need to overwrite the untracked pyc files that were created. If you get errors
such as these
hplip: /usr/share/hplip/base/__pycache__/__init__.cpython-38.pyc exists in filesystem
hplip: /usr/share/hplip/base/__pycache__/avahi.cpython-38.pyc exists in filesystem
hplip: /usr/share/hplip/base/__pycache__/codes.cpython-38.pyc exists in filesystem
...many more...

when updating, use
pacman -Suy --overwrite /usr/share/hplip/\*

to perform the upgrade.

2020-04-13 nss>=3.51.1-1 and lib32-nss>=3.51.1-1 updates require manual intervention
The nss and lib32-nss packages prior to version 3.51.1-1 were missing a soname link each. This has been fixed in 3.51.1-1, so the upgrade will need to overwrite the untracked files created by ldconfig. If you get any of these errors
nss: /usr/lib/p11-kit-trust.so exists in filesystem
lib32-nss: /usr/lib32/p11-kit-trust.so exists in filesystem

when updating, use
pacman -Syu --overwrite /usr/lib\*/p11-kit-trust.so

to perform the upgrade.

2020-04-14 zn_poly 0.9.2-2 update requires manual intervention
The zn_poly package prior to version 0.9.2-2 was missing a soname link.
This has been fixed in 0.9.2-2, so the upgrade will need to overwrite the
untracked files created by ldconfig. If you get an error
zn_poly: /usr/lib/libzn_poly-0.9.so exists in filesystem

when updating, use
pacman -Syu --overwrite usr/lib/libzn_poly-0.9.so

to perform the upgrade.


View File

@ -0,0 +1,3 @@
2020-04-13 nss>=3.51.1-1 and lib32-nss>=3.51.1-1 updates require manual intervention
2020-04-14 zn_poly 0.9.2-2 update requires manual intervention

View File

@ -0,0 +1,3 @@
2020-04-14 zn_poly 0.9.2-2 update requires manual intervention
2020-04-13 nss>=3.51.1-1 and lib32-nss>=3.51.1-1 updates require manual intervention

View File

@ -0,0 +1,3 @@
2020-04-14 zn_poly 0.9.2-2 update requires manual intervention
The zn_poly package prior to version 0.9.2-2 was missing a soname link.

174
pkg/news/news.go Normal file
View File

@ -0,0 +1,174 @@
package news
import (
"bytes"
"context"
"encoding/xml"
"html"
"io"
"net/http"
"strings"
"time"
"github.com/Jguer/yay/v12/pkg/text"
)
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) printNews(logger *text.Logger, buildTime time.Time, all, quiet bool) {
var fd string
date, err := time.Parse(time.RFC1123Z, item.PubDate)
if err != nil {
logger.Errorln(err)
} else {
fd = text.FormatTime(int(date.Unix()))
if !all && !buildTime.IsZero() {
if buildTime.After(date) {
return
}
}
}
logger.Println(text.Bold(text.Magenta(fd)), text.Bold(strings.TrimSpace(item.Title)))
if !quiet {
desc := strings.TrimSpace(parseNews(item.Description))
logger.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(ctx context.Context, client *http.Client, logger *text.Logger,
cutOffDate time.Time, bottomUp, all, quiet bool,
) error {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://archlinux.org/feeds/news", http.NoBody)
if err != nil {
return err
}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
rssGot := rss{}
d := xml.NewDecoder(bytes.NewReader(body))
if err := d.Decode(&rssGot); err != nil {
return err
}
if bottomUp {
for i := len(rssGot.Channel.Items) - 1; i >= 0; i-- {
rssGot.Channel.Items[i].printNews(logger, cutOffDate, all, quiet)
}
} else {
for i := 0; i < len(rssGot.Channel.Items); i++ {
rssGot.Channel.Items[i].printNews(logger, cutOffDate, all, quiet)
}
}
return nil
}
// 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
tagBuffer bytes.Buffer
escapeBuffer bytes.Buffer
inTag = false
inEscape = false
)
for _, char := range str {
if inTag {
if char == '>' {
inTag = false
switch tagBuffer.String() {
case "code":
buffer.WriteString(text.CyanCode)
case "/code":
buffer.WriteString(text.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(text.ResetCode)
return buffer.String()
}

179
pkg/news/news_test.go Normal file
View File

@ -0,0 +1,179 @@
//go:build !integration
// +build !integration
package news
import (
"context"
"io"
"net/http"
"os"
"strings"
"testing"
"time"
"github.com/bradleyjkemp/cupaloy"
"github.com/stretchr/testify/assert"
"gopkg.in/h2non/gock.v1"
"github.com/Jguer/yay/v12/pkg/text"
)
const lastNews = `
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<channel>
<title>Arch Linux: Recent news updates</title>
<link>https://www.archlinux.org/news/</link>
<description>The latest and greatest news from the Arch Linux distribution.</description>
<atom:link href="https://www.archlinux.org/feeds/news/" rel="self" />
<language>en-us</language>
<lastBuildDate>Tue, 14 Apr 2020 16:30:32 +0000</lastBuildDate>
<item>
<title>zn_poly 0.9.2-2 update requires manual intervention</title>
<link>https://www.archlinux.org/news/zn_poly-092-2-update-requires-manual-intervention/</link>
<description>&lt;p&gt;The zn_poly package prior to version 0.9.2-2 was missing a soname link.</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Antonio Rojas</dc:creator>
<pubDate>Tue, 14 Apr 2020 16:30:30 +0000</pubDate>
<guid isPermaLink="false">tag:www.archlinux.org,2020-04-14:/news/zn_poly-092-2-update-requires-manual-intervention/</guid>
</item>
</channel>
</rss>
`
const sampleNews = `<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Arch Linux: Recent news updates</title><link>https://www.archlinux.org/news/</link><description>The latest and greatest news from the Arch Linux distribution.</description><atom:link href="https://www.archlinux.org/feeds/news/" rel="self"></atom:link><language>en-us</language><lastBuildDate>Tue, 14 Apr 2020 16:30:32 +0000</lastBuildDate><item><title>zn_poly 0.9.2-2 update requires manual intervention</title><link>https://www.archlinux.org/news/zn_poly-092-2-update-requires-manual-intervention/</link><description>&lt;p&gt;The zn_poly package prior to version 0.9.2-2 was missing a soname link.
This has been fixed in 0.9.2-2, so the upgrade will need to overwrite the
untracked files created by ldconfig. If you get an error&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;zn_poly: /usr/lib/libzn_poly-0.9.so exists in filesystem
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;when updating, use&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pacman -Syu --overwrite usr/lib/libzn_poly-0.9.so
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to perform the upgrade.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Antonio Rojas</dc:creator><pubDate>Tue, 14 Apr 2020 16:30:30 +0000</pubDate><guid isPermaLink="false">tag:www.archlinux.org,2020-04-14:/news/zn_poly-092-2-update-requires-manual-intervention/</guid></item><item><title>nss&gt;=3.51.1-1 and lib32-nss&gt;=3.51.1-1 updates require manual intervention</title><link>https://www.archlinux.org/news/nss3511-1-and-lib32-nss3511-1-updates-require-manual-intervention/</link><description>&lt;p&gt;The nss and lib32-nss packages prior to version 3.51.1-1 were missing a soname link each. This has been fixed in 3.51.1-1, so the upgrade will need to overwrite the untracked files created by ldconfig. If you get any of these errors&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nss: /usr/lib/p11-kit-trust.so exists in filesystem
lib32-nss: /usr/lib32/p11-kit-trust.so exists in filesystem
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;when updating, use&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pacman -Syu --overwrite /usr/lib\*/p11-kit-trust.so
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to perform the upgrade.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Jan Alexander Steffens</dc:creator><pubDate>Mon, 13 Apr 2020 00:35:58 +0000</pubDate><guid isPermaLink="false">tag:www.archlinux.org,2020-04-13:/news/nss3511-1-and-lib32-nss3511-1-updates-require-manual-intervention/</guid></item><item><title>hplip 3.20.3-2 update requires manual intervention</title><link>https://www.archlinux.org/news/hplip-3203-2-update-requires-manual-intervention/</link><description>&lt;p&gt;The hplip package prior to version 3.20.3-2 was missing the compiled
python modules. This has been fixed in 3.20.3-2, so the upgrade will
need to overwrite the untracked pyc files that were created. If you get errors
such as these&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hplip: /usr/share/hplip/base/__pycache__/__init__.cpython-38.pyc exists in filesystem
hplip: /usr/share/hplip/base/__pycache__/avahi.cpython-38.pyc exists in filesystem
hplip: /usr/share/hplip/base/__pycache__/codes.cpython-38.pyc exists in filesystem
...many more...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;when updating, use&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pacman -Suy --overwrite /usr/share/hplip/\*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to perform the upgrade.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Andreas Radke</dc:creator><pubDate>Thu, 19 Mar 2020 06:53:30 +0000</pubDate><guid isPermaLink="false">tag:www.archlinux.org,2020-03-19:/news/hplip-3203-2-update-requires-manual-intervention/</guid></item><item><title>firewalld&gt;=0.8.1-2 update requires manual intervention</title><link>https://www.archlinux.org/news/firewalld081-2-update-requires-manual-intervention/</link><description>&lt;p&gt;The firewalld package prior to version 0.8.1-2 was missing the compiled python modules. This has been fixed in 0.8.1-2, so the upgrade will need to overwrite the untracked pyc files created. If you get errors like these&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;firewalld: /usr/lib/python3.8/site-packages/firewall/__pycache__/__init__.cpython-38.pyc exists in filesystem
firewalld: /usr/lib/python3.8/site-packages/firewall/__pycache__/client.cpython-38.pyc exists in filesystem
firewalld: /usr/lib/python3.8/site-packages/firewall/__pycache__/dbus_utils.cpython-38.pyc exists in filesystem
...many more...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;when updating, use&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pacman -Suy --overwrite /usr/lib/python3.8/site-packages/firewall/\*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to perform the upgrade.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Jan Alexander Steffens</dc:creator><pubDate>Sun, 01 Mar 2020 16:36:48 +0000</pubDate><guid isPermaLink="false">tag:www.archlinux.org,2020-03-01:/news/firewalld081-2-update-requires-manual-intervention/</guid></item><item><title>The Future of the Arch Linux Project Leader</title><link>https://www.archlinux.org/news/the-future-of-the-arch-linux-project-leader/</link><description>&lt;p&gt;Hello everyone,&lt;/p&gt;
&lt;p&gt;Some of you may know me from the days when I was much more involved in Arch, but most of you probably just know me as a name on the website. Ive been with Arch for some time, taking the leadership of this beast over from Judd back in 2007. But, as these things often go, my involvement has slid down to minimal levels over time. Its high time that changes.&lt;/p&gt;
&lt;p&gt;Arch Linux needs involved leadership to make hard decisions and direct the project where it needs to go. And I am not in a position to do this.&lt;/p&gt;
&lt;p&gt;In a team effort, the Arch Linux staff devised a new process for determining future leaders. From now on, leaders will be elected by the staff for a term length of two years. Details of this new process can be found &lt;a href="https://wiki.archlinux.org/index.php/DeveloperWiki:Project_Leader"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the first official vote with Levente Polyak (anthraxx), Gaetan Bisson (vesath), Giancarlo Razzolini (grazzolini), and Sven-Hendrik Haase (svenstaro) as candidates, and through 58 verified votes, a winner was chosen:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Levente Polyak (anthraxx) will be taking over the reins of this ship. Congratulations!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Thanks for everything over all these years,&lt;br /&gt;
Aaron Griffin (phrakture)&lt;/em&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Aaron Griffin</dc:creator><pubDate>Mon, 24 Feb 2020 15:56:28 +0000</pubDate><guid isPermaLink="false">tag:www.archlinux.org,2020-02-24:/news/the-future-of-the-arch-linux-project-leader/</guid></item><item><title>Planet Arch Linux migration</title><link>https://www.archlinux.org/news/planet-arch-linux-migration/</link><description>&lt;p&gt;The software behind planet.archlinux.org was implemented in Python 2 and is no longer maintained upstream. This functionality has now been implemented in archlinux.org's archweb backend which is actively maintained but offers a slightly different experience.&lt;/p&gt;
&lt;p&gt;The most notable changes are the offered feeds and the feed location. Archweb only offers an Atom feed which is located at &lt;a href="https://archlinux.org/feeds/planet"&gt;here&lt;/a&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Jelle van der Waa</dc:creator><pubDate>Sat, 22 Feb 2020 22:43:00 +0000</pubDate><guid isPermaLink="false">tag:www.archlinux.org,2020-02-22:/news/planet-arch-linux-migration/</guid></item><item><title>sshd needs restarting after upgrading to openssh-8.2p1</title><link>https://www.archlinux.org/news/sshd-needs-restarting-after-upgrading-to-openssh-82p1/</link><description>&lt;p&gt;After upgrading to openssh-8.2p1, the existing SSH daemon will be unable to accept new connections. (See &lt;a href="https://bugs.archlinux.org/task/65517"&gt;FS#65517&lt;/a&gt;.) When upgrading remote hosts, please make sure to restart the SSH daemon using &lt;code&gt;systemctl restart sshd&lt;/code&gt; right after running &lt;code&gt;pacman -Syu&lt;/code&gt;. If you are upgrading to openssh-8.2p1-3 or higher, this restart will happen automatically.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Gaetan Bisson</dc:creator><pubDate>Mon, 17 Feb 2020 01:35:04 +0000</pubDate><guid isPermaLink="false">tag:www.archlinux.org,2020-02-17:/news/sshd-needs-restarting-after-upgrading-to-openssh-82p1/</guid></item><item><title>rsync compatibility</title><link>https://www.archlinux.org/news/rsync-compatibility/</link><description>&lt;p&gt;Our &lt;code&gt;rsync&lt;/code&gt; package was shipped with bundled &lt;code&gt;zlib&lt;/code&gt; to provide compatibility
with the old-style &lt;code&gt;--compress&lt;/code&gt; option up to version 3.1.0. Version 3.1.1 was
released on 2014-06-22 and is shipped by all major distributions now.&lt;/p&gt;
&lt;p&gt;So we decided to finally drop the bundled library and ship a package with
system &lt;code&gt;zlib&lt;/code&gt;. This also fixes security issues, actual ones and in future. Go
and blame those running old versions if you encounter errors with &lt;code&gt;rsync
3.1.3-3&lt;/code&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Christian Hesse</dc:creator><pubDate>Wed, 15 Jan 2020 20:14:43 +0000</pubDate><guid isPermaLink="false">tag:www.archlinux.org,2020-01-15:/news/rsync-compatibility/</guid></item><item><title>Now using Zstandard instead of xz for package compression</title><link>https://www.archlinux.org/news/now-using-zstandard-instead-of-xz-for-package-compression/</link><description>&lt;p&gt;As announced on the &lt;a href="https://lists.archlinux.org/pipermail/arch-dev-public/2019-December/029752.html"&gt;mailing list&lt;/a&gt;, on Friday, Dec 27 2019, our package compression scheme has changed from xz (.pkg.tar.xz) to &lt;a href="https://lists.archlinux.org/pipermail/arch-dev-public/2019-December/029778.html"&gt;zstd (.pkg.tar.zst)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;zstd and xz trade blows in their compression ratio. Recompressing all packages to zstd with our options yields a total ~0.8% increase in package size on all of our packages combined, but the decompression time for all packages saw a ~1300% speedup.&lt;/p&gt;
&lt;p&gt;We already have more than 545 zstd-compressed packages in our repositories, and as packages get updated more will keep rolling in. We have not found any user-facing issues as of yet, so things appear to be working.&lt;/p&gt;
&lt;p&gt;As a packager, you will automatically start building .pkg.tar.zst packages if you are using the latest version of devtools (&amp;gt;= 20191227).&lt;br /&gt;
As an end-user no manual intervention is required, assuming that you have read and followed the news post &lt;a href="https://www.archlinux.org/news/required-update-to-recent-libarchive/"&gt;from late last year&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you nevertheless haven't updated libarchive since 2018, all hope is not lost! Binary builds of pacman-static are available from Eli Schwartz' &lt;a href="https://wiki.archlinux.org/index.php/Unofficial_user_repositories#eschwartz"&gt;personal repository&lt;/a&gt; (or direct link to &lt;a href="https://pkgbuild.com/~eschwartz/repo/x86_64-extracted/"&gt;binary&lt;/a&gt;), signed with their Trusted User keys, with which you can perform the update.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Robin Broda</dc:creator><pubDate>Sat, 04 Jan 2020 20:35:55 +0000</pubDate><guid isPermaLink="false">tag:www.archlinux.org,2020-01-04:/news/now-using-zstandard-instead-of-xz-for-package-compression/</guid></item><item><title>Xorg cleanup requires manual intervention</title><link>https://www.archlinux.org/news/xorg-cleanup-requires-manual-intervention/</link><description>&lt;p&gt;In the process of &lt;a href="https://bugs.archlinux.org/task/64892"&gt;Xorg cleanup&lt;/a&gt; the update requires manual
intervention when you hit this message:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:: installing xorgproto (2019.2-2) breaks dependency 'inputproto' required by lib32-libxi
:: installing xorgproto (2019.2-2) breaks dependency 'dmxproto' required by libdmx
:: installing xorgproto (2019.2-2) breaks dependency 'xf86dgaproto' required by libxxf86dga
:: installing xorgproto (2019.2-2) breaks dependency 'xf86miscproto' required by libxxf86misc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;when updating, use: &lt;code&gt;pacman -Rdd libdmx libxxf86dga libxxf86misc &amp;amp;&amp;amp; pacman -Syu&lt;/code&gt; to perform the upgrade.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Andreas Radke</dc:creator><pubDate>Fri, 20 Dec 2019 13:37:40 +0000</pubDate><guid isPermaLink="false">tag:www.archlinux.org,2019-12-20:/news/xorg-cleanup-requires-manual-intervention/</guid></item></channel></rss>
`
func TestPrintNewsFeed(t *testing.T) {
layout := "2006-01-02"
str := "2020-04-13"
lastNewsTime, _ := time.Parse(layout, str)
type args struct {
cutOffDate time.Time
bottomUp bool
all bool
quiet bool
}
tests := []struct {
name string
args args
wantErr bool
}{
{name: "all-verbose", args: args{bottomUp: true, cutOffDate: time.Now(), all: true, quiet: false}, wantErr: false},
{name: "all-quiet", args: args{bottomUp: true, cutOffDate: lastNewsTime, all: true, quiet: true}, wantErr: false},
{name: "latest-quiet", args: args{bottomUp: true, cutOffDate: lastNewsTime, all: false, quiet: true}, wantErr: false},
{name: "latest-quiet-topdown", args: args{bottomUp: false, cutOffDate: lastNewsTime, all: false, quiet: true}, wantErr: false},
}
t.Setenv("TZ", "UTC")
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
gock.New("https://archlinux.org").
Get("/feeds/news").
Reply(200).
BodyString(sampleNews)
defer gock.Off()
r, w, _ := os.Pipe()
logger := text.NewLogger(w, w, strings.NewReader(""), false, "logger")
err := PrintNewsFeed(context.Background(), &http.Client{}, logger,
tt.args.cutOffDate, tt.args.bottomUp, tt.args.all, tt.args.quiet)
assert.NoError(t, err)
w.Close()
out, _ := io.ReadAll(r)
cupaloy.SnapshotT(t, out)
})
}
}
// GIVEN last build time at 13h00
// WHEN there's a news posted at 18h00
// THEN it should still be printed
func TestPrintNewsFeedSameDay(t *testing.T) {
str := "2020-04-14T13:04:05Z"
lastNewsTime, _ := time.Parse(time.RFC3339, str)
gock.New("https://archlinux.org").
Get("/feeds/news").
Reply(200).
BodyString(lastNews)
defer gock.Off()
r, w, _ := os.Pipe()
logger := text.NewLogger(w, w, strings.NewReader(""), false, "logger")
err := PrintNewsFeed(context.Background(), &http.Client{}, logger,
lastNewsTime, true, false, false)
assert.NoError(t, err)
w.Close()
out, _ := io.ReadAll(r)
cupaloy.SnapshotT(t, out)
}

110
pkg/query/aur_warnings.go Normal file
View File

@ -0,0 +1,110 @@
package query
import (
"strings"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/aur"
"github.com/Jguer/go-alpm/v2"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/text"
)
type AURWarnings struct {
Orphans []string
OutOfDate []string
Missing []string
LocalNewer []string
log *text.Logger
}
func NewWarnings(logger *text.Logger) *AURWarnings {
return &AURWarnings{log: logger}
}
func (warnings *AURWarnings) AddToWarnings(remote map[string]alpm.IPackage, aurPkg *aur.Pkg) {
name := aurPkg.Name
pkg, ok := remote[name]
if !ok {
return
}
if aurPkg.Maintainer == "" && !pkg.ShouldIgnore() {
warnings.Orphans = append(warnings.Orphans, name)
}
if aurPkg.OutOfDate != 0 && !pkg.ShouldIgnore() {
warnings.OutOfDate = append(warnings.OutOfDate, name)
}
if !pkg.ShouldIgnore() && !isDevelPackage(pkg) && db.VerCmp(pkg.Version(), aurPkg.Version) > 0 {
left, right := GetVersionDiff(pkg.Version(), aurPkg.Version)
newerMsg := gotext.Get("%s: local (%s) is newer than AUR (%s)",
text.Cyan(name),
left, right,
)
warnings.LocalNewer = append(warnings.LocalNewer, newerMsg)
}
}
func (warnings *AURWarnings) CalculateMissing(remoteNames []string,
remote map[string]alpm.IPackage, aurData map[string]*aur.Pkg,
) {
for _, name := range remoteNames {
if _, ok := aurData[name]; !ok && !remote[name].ShouldIgnore() {
if _, ok := aurData[strings.TrimSuffix(name, "-debug")]; !ok {
warnings.Missing = append(warnings.Missing, name)
}
}
}
}
func (warnings *AURWarnings) Print() {
normalMissing, debugMissing := filterDebugPkgs(warnings.Missing)
if len(normalMissing) > 0 {
warnings.log.Warnln(gotext.Get("Packages not in AUR:"), formatNames(normalMissing))
}
if len(debugMissing) > 0 {
warnings.log.Warnln(gotext.Get("Missing AUR Debug Packages:"), formatNames(debugMissing))
}
if len(warnings.Orphans) > 0 {
warnings.log.Warnln(gotext.Get("Orphan (unmaintained) AUR Packages:"), formatNames(warnings.Orphans))
}
if len(warnings.OutOfDate) > 0 {
warnings.log.Warnln(gotext.Get("Flagged Out Of Date AUR Packages:"), formatNames(warnings.OutOfDate))
}
if len(warnings.LocalNewer) > 0 {
for _, newer := range warnings.LocalNewer {
warnings.log.Warnln(newer)
}
}
}
func filterDebugPkgs(names []string) (normal, debug []string) {
normal = make([]string, 0, len(names))
debug = make([]string, 0, len(names))
for _, name := range names {
if strings.HasSuffix(name, "-debug") {
debug = append(debug, name)
} else {
normal = append(normal, name)
}
}
return
}
func formatNames(names []string) string {
return " " + text.Cyan(strings.Join(names, " "))
}

21
pkg/query/errors.go Normal file
View File

@ -0,0 +1,21 @@
package query
import (
"github.com/leonelquinteros/gotext"
)
// ErrAURSearch means that it was not possible to connect to the AUR.
type ErrAURSearch struct {
inner error
}
func (e ErrAURSearch) Error() string {
return gotext.Get("Error during AUR search: %s\n", e.inner.Error())
}
// ErrNoQuery means that query was not executed.
type ErrNoQuery struct{}
func (e ErrNoQuery) Error() string {
return gotext.Get("no query was executed")
}

30
pkg/query/filter.go Normal file
View File

@ -0,0 +1,30 @@
package query
import (
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
)
func RemoveInvalidTargets(logger *text.Logger, targets []string, mode parser.TargetMode) []string {
filteredTargets := make([]string, 0)
for _, target := range targets {
dbName, _ := text.SplitDBFromName(target)
if dbName == "aur" && !mode.AtLeastAUR() {
logger.Warnln(gotext.Get("%s: can't use target with option --repo -- skipping", text.Cyan(target)))
continue
}
if dbName != "aur" && dbName != "" && !mode.AtLeastRepo() {
logger.Warnln(gotext.Get("%s: can't use target with option --aur -- skipping", text.Cyan(target)))
continue
}
filteredTargets = append(filteredTargets, target)
}
return filteredTargets
}

89
pkg/query/metric.go Normal file
View File

@ -0,0 +1,89 @@
package query
import (
"hash/fnv"
"strings"
"github.com/adrg/strutil"
)
const minVotes = 30
// TODO: Add support for Popularity and LastModified
func (a *abstractResults) aurSortByMetric(pkg *abstractResult) float64 {
return 1 - (minVotes / (minVotes + float64(pkg.votes)))
}
func (a *abstractResults) GetMetric(pkg *abstractResult) float64 {
if v, ok := a.distanceCache[pkg.name]; ok {
return v
}
if strings.EqualFold(pkg.name, a.search) {
return 1.0
}
sim := strutil.Similarity(pkg.name, a.search, a.metric)
for _, prov := range pkg.provides {
// If the package provides search, it's a perfect match
// AUR packages don't populate provides
candidate := strutil.Similarity(prov, a.search, a.metric) * 0.80
if candidate > sim {
sim = candidate
}
}
simDesc := strutil.Similarity(pkg.description, a.search, a.metric)
// slightly overweight sync sources by always giving them max popularity
popularity := 1.0
if pkg.source == sourceAUR {
popularity = a.aurSortByMetric(pkg)
}
sim = sim*0.5 + simDesc*0.2 + popularity*0.3
a.distanceCache[pkg.name] = sim
return sim
}
func (a *abstractResults) separateSourceScore(source string, score float64) float64 {
if !a.separateSources {
return 0
}
if score == 1.0 {
return 50
}
switch source {
case sourceAUR:
return 0
case "core":
return 40
case "extra":
return 30
case "community":
return 20
case "multilib":
return 10
}
if v, ok := a.separateSourceCache[source]; ok {
return v
}
h := fnv.New32a()
h.Write([]byte(source))
sourceScore := float64(int(h.Sum32())%9 + 2)
a.separateSourceCache[source] = sourceScore
return sourceScore
}
func (a *abstractResults) calculateMetric(pkg *abstractResult) float64 {
score := a.GetMetric(pkg)
return a.separateSourceScore(pkg.source, score) + score
}

Some files were not shown because too many files have changed in this diff Show More